现在我正在通过s = window.getSelection()
和range = s.getRangeAt(0)
(浏览器撇开)捕获用户的文字选择。每当选择 <p>
时,我就可以轻松致电range.surroundContents(document.createElement("em"))
以使所选文字包裹并带有<em>
代码
但是,在这个例子中,
<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>
有什么想法吗?
仅供参考:我一直在尝试Range
API,但我看不到实现这一结果的简单方法。与
var r = document.createRange();
r.setStart(range.startContainer, range.startOffset);
r.setEnd(range.endContainer, range.endOffset+40);
selection.addRange(r);
我最终可以通过重新定位偏移来破解某些东西,但仅限于“开始”和“结束”容器! (即在这种情况下是Bar
段,我该如何包装它?)
答案 0 :(得分:9)
您是否尝试过以下方法(实际上是W3C规范中关于surroundContents应该做什么的描述):
var wrappingNode = document.createElement("div");
wrappingNode.appendChild(range.extractContents());
range.insertNode(wrappingNode);
答案 1 :(得分:3)
我目前正在使用内联编辑器,我编写了一个函数,可以使用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;
}
};
这是一个带有一些复杂HTML的JSFiddle作为demo:http://jsfiddle.net/mjf9K/1/。请注意,我直接从我的申请中删除了。我使用一些助手来正确地将范围恢复到原始选择等。这些不包括在内。
答案 2 :(得分:1)
这是当你将contentEditable = true属性添加到这些段落的父级时,选择任何文本,甚至跨段落,然后拨打电话
document.execCommand('italic', false, null);
最后如果需要,将contentEditable属性设置为false。
顺便说一句,这也适用于IE,除了要进入可编辑模式,我认为它被称为designMode或其他东西,google for it。