I'm working on a rich text editor like web application, basically a XML editor written in javascript.
My javascript code needs to wrap a selection of nodes from the contentEditable div container. I'm using the methods described at MDC. But since I need to synchronize the div containers content to my XML DOM I would like to avoid partial selections as described in w3c ranges:
<BODY><H1>Title</H1><P>Blah xyz.</P></BODY
............^----------------^............
This selection starts inside H1 and ends inside P, I'd like it to include H1,P completely.
Is there an easy way to extend the selection to cover partially selected children completely? Basically I want to use range.surroundContents() without running into an exception.
(The code doesn't need to work with opera/IE)
Looking at the MDC documentation, I manage do something like this:
Selection.prototype.coverAll = function() {
var ranges = [];
for(var i=0; i<this.rangeCount; i++) {
var range = this.getRangeAt(i);
while(range.startContainer.nodeType == 3
|| range.startContainer.childNodes.length == 1)
range.setStartBefore(range.startContainer);
while(range.endContainer.nodeType == 3
|| range.endContainer.childNodes.length == 1)
range.setEndAfter(range.endContainer);
ranges.push(range);
}
this.removeAllRanges();
for(var i=0; i<ranges.length; i++) {
this.addRange(ranges[i]);
}
return;
};
You can try it here : http://jsfiddle.net/GFuX6/9/
edit: Updated to have the browser display correctly the augmented selection. It does what you asked for, even if the selection contains several ranges (with Ctrl).
To make several partial nodes Bold, here is a solution:
Selection.prototype.boldinize = function() {
this.coverAll();
for(var i=0; i<this.rangeCount; i++) {
var range = this.getRangeAt(i);
var parent = range.commonAncestorContainer;
var b = document.createElement('b');
if(parent.nodeType == 3) {
range.surroundContents(b);
} else {
var content = range.extractContents();
b.appendChild(content);
range.insertNode(b);
}
}
};
Thanks to Alsciende I finally came up with the code at http://jsfiddle.net/wesUV/21/. This method isn't as greedy as the other one. After coverAll(), surroundContents() should always work.
Selection.prototype.coverAll = function() {
var ranges = [];
for(var i=0; i<this.rangeCount; i++) {
var range = this.getRangeAt(i);
var ancestor = range.commonAncestorContainer;
if (ancestor.nodeType == 1) {
if (range.startContainer.parentNode != ancestor && this.containsNode(range.startContainer.parentNode, true)) {
range.setStartBefore(range.startContainer.parentNode);
}
if (range.endContainer.parentNode != ancestor && this.containsNode(range.endContainer.parentNode, true)) {
range.setEndAfter(range.endContainer.parentNode);
}
}
ranges.push(range);
}
this.removeAllRanges();
for(var i=0; i<ranges.length; i++) {
this.addRange(ranges[i]);
}
return;
};
And the boldinize function:
Selection.prototype.boldinize = function() {
for(var i=0; i<this.rangeCount; i++) {
var range = this.getRangeAt(i);
var b = document.createElement('b');
try {
range.surroundContents(b);
} catch (e) {
alert(e);
}
}
};
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With