var wrappingNode = document.createElement("div");
wrappingNode.appendChild(range.extractContents());
range.insertNode(wrappingNode);
目前,我正在通过s = window.getSelection()
和range = s.getRangeAt(0)
捕获用户的文本选择(浏览器的提示不留)。每当在[[0]之内]进行选择<p>
时,我都可以轻松地调用range.surroundContents(document.createElement("em"))
以使所选择的文本带有<em>
标记wrapped。但是,在此示例中,><p>This is the Foo paragraph.</p>
<p>This is the Bar paragraph.</p>
<p>This is the Baz paragraph.</p>
[当用户从
Foo
到Baz
进行文本选择时,我无法调用range.surroundContents
:Firefox失败The boundary-points of a range does not meet specific requirements." code: "1
,因为选择无效的HTML。
在那种情况下,我想以某种方式在DOM中获得以下状态:
<p>This is the <em>Foo paragraph.</em></p>
<p><em>This is the Bar paragraph.</em></p>
<p><em>This is the Baz</em> paragraph.</p>
有什么想法吗?
FYI:我一直在尝试使用Range
API,但看不到实现该结果的直接方法。带有
var r = document.createRange();
r.setStart(range.startContainer, range.startOffset);
r.setEnd(range.endContainer, range.endOffset+40);
selection.addRange(r);
我最终可以通过重新定位偏移量来破解某些东西,但仅适用于'start'和'end'容器! (即,在这种情况下是Bar
段,如何包装?)
内进行选择时,我都可以轻松地调用...
var wrappingNode = document.createElement("div");
wrappingNode.appendChild(range.extractContents());
range.insertNode(wrappingNode);
我目前正在使用内联编辑器,并且我编写了一个函数,该函数可以像execCommand一样用任何类型的元素正确包装跨元素范围。
function surroundSelection(elementType) { function getAllDescendants (node, callback) { for (var i = 0; i < node.childNodes.length; i++) { var child = node.childNodes[i]; getAllDescendants(child, callback); callback(child); } } function glueSplitElements (firstEl, secondEl){ var done = false, result = []; if(firstEl === undefined || secondEl === undefined){ return false; } if(firstEl.nodeName === secondEl.nodeName){ result.push([firstEl, secondEl]); while(!done){ firstEl = firstEl.childNodes[firstEl.childNodes.length - 1]; secondEl = secondEl.childNodes[0]; if(firstEl === undefined || secondEl === undefined){ break; } if(firstEl.nodeName !== secondEl.nodeName){ done = true; } else { result.push([firstEl, secondEl]); } } } for(var i = result.length - 1; i >= 0; i--){ var elements = result[i]; while(elements[1].childNodes.length > 0){ elements[0].appendChild(elements[1].childNodes[0]); } elements[1].parentNode.removeChild(elements[1]); } } // abort in case the given elemenType doesn't exist. try { document.createElement(elementType); } catch (e){ return false; } var selection = getSelection(); if(selection.rangeCount > 0){ var range = selection.getRangeAt(0), rangeContents = range.extractContents(), nodesInRange = rangeContents.childNodes, nodesToWrap = []; for(var i = 0; i < nodesInRange.length; i++){ if(nodesInRange[i].nodeName.toLowerCase() === "#text"){ nodesToWrap.push(nodesInRange[i]); } else { getAllDescendants(nodesInRange[i], function(child){ if(child.nodeName.toLowerCase() === "#text"){ nodesToWrap.push(child); } }); } }; for(var i = 0; i < nodesToWrap.length; i++){ var child = nodesToWrap[i], wrap = document.createElement(elementType); if(child.nodeValue.replace(/(\s|\n|\t)/g, "").length !== 0){ child.parentNode.insertBefore(wrap, child); wrap.appendChild(child); } else { wrap = null; } } var firstChild = rangeContents.childNodes[0]; var lastChild = rangeContents.childNodes[rangeContents.childNodes.length - 1]; range.insertNode(rangeContents); glueSplitElements(firstChild.previousSibling, firstChild); glueSplitElements(lastChild, lastChild.nextSibling); rangeContents = null; } };
这是一个JSFiddle,带有一些复杂的HTML作为演示:http://jsfiddle.net/mjf9K/1/。请注意,我直接从应用程序中删除了此内容。我使用一些帮助程序将范围正确恢复到原始选择等。这些不包括在内。
document.execCommand('italic', false, null);
最后,如果需要,将contentEditable属性设置为false。
顺便说一句,这也适用于IE,但要进入可编辑模式,我认为它被称为designMode或类似的东西,用google搜索。
var wrappingNode = document.createElement("div");
wrappingNode.appendChild(range.extractContents());
range.insertNode(wrappingNode);
function surroundSelection(elementType) {
function getAllDescendants (node, callback) {
for (var i = 0; i < node.childNodes.length; i++) {
var child = node.childNodes[i];
getAllDescendants(child, callback);
callback(child);
}
}
function glueSplitElements (firstEl, secondEl){
var done = false,
result = [];
if(firstEl === undefined || secondEl === undefined){
return false;
}
if(firstEl.nodeName === secondEl.nodeName){
result.push([firstEl, secondEl]);
while(!done){
firstEl = firstEl.childNodes[firstEl.childNodes.length - 1];
secondEl = secondEl.childNodes[0];
if(firstEl === undefined || secondEl === undefined){
break;
}
if(firstEl.nodeName !== secondEl.nodeName){
done = true;
} else {
result.push([firstEl, secondEl]);
}
}
}
for(var i = result.length - 1; i >= 0; i--){
var elements = result[i];
while(elements[1].childNodes.length > 0){
elements[0].appendChild(elements[1].childNodes[0]);
}
elements[1].parentNode.removeChild(elements[1]);
}
}
// abort in case the given elemenType doesn't exist.
try {
document.createElement(elementType);
} catch (e){
return false;
}
var selection = getSelection();
if(selection.rangeCount > 0){
var range = selection.getRangeAt(0),
rangeContents = range.extractContents(),
nodesInRange = rangeContents.childNodes,
nodesToWrap = [];
for(var i = 0; i < nodesInRange.length; i++){
if(nodesInRange[i].nodeName.toLowerCase() === "#text"){
nodesToWrap.push(nodesInRange[i]);
} else {
getAllDescendants(nodesInRange[i], function(child){
if(child.nodeName.toLowerCase() === "#text"){
nodesToWrap.push(child);
}
});
}
};
for(var i = 0; i < nodesToWrap.length; i++){
var child = nodesToWrap[i],
wrap = document.createElement(elementType);
if(child.nodeValue.replace(/(\s|\n|\t)/g, "").length !== 0){
child.parentNode.insertBefore(wrap, child);
wrap.appendChild(child);
} else {
wrap = null;
}
}
var firstChild = rangeContents.childNodes[0];
var lastChild = rangeContents.childNodes[rangeContents.childNodes.length - 1];
range.insertNode(rangeContents);
glueSplitElements(firstChild.previousSibling, firstChild);
glueSplitElements(lastChild, lastChild.nextSibling);
rangeContents = null;
}
};
document.execCommand('italic', false, null);