Javascript 用JS/jQuery中的文本替换DOM树中的非图像元素
我想用文本替换Javascript 用JS/jQuery中的文本替换DOM树中的非图像元素,javascript,jquery,dom,contenteditable,Javascript,Jquery,Dom,Contenteditable,我想用文本替换contenteditable中的非img元素。但是,我想保留任何img元素,包括嵌套在其他元素中的元素。换言之: 给定输入,例如: <div><span>Foo <strong>Bar <img src="blah.png"></strong> and more text <img src="another.png"></span> With some other text <img src
contenteditable
中的非img
元素。但是,我想保留任何img
元素,包括嵌套在其他元素中的元素。换言之:
给定输入,例如:
<div><span>Foo <strong>Bar <img src="blah.png"></strong> and more text <img src="another.png"></span> With some other text <img src="yetmore.png"></div>
Foo栏和更多包含其他文本的文本
我想提出:
Foo Bar <img src="blah.png"> and more text <img src="another.png"> With some other text <img src="yetmore.png">
Foo-Bar和更多带有其他文本的文本
由于这是一个contenteditable
,我不想使用innerHTML
读/写,因为这样会丢失光标位置等(恢复它很困难,因为最终会使用不同的DOM树,所以选择节点会丢失)
我的最佳选择是在树上迭代并手动拆分和连接文本节点等等吗?我希望有一个更好的方法,或者一个图书馆可以做这样的事情
$(document).ready(function(){
replaceChildren($('#contenteditable'));
});
function replaceChildren(elem){
$(elem).children("*").not("img").each(function(){
if ($(this).children("*").not("img").length>0){
replaceChildren(this);
}
$(this).after($(this).html());
$(this).remove();
});
}
某种递归解
某种递归解决方案。对根元素进行浅克隆。沿着原始元素树走下去,收集文本。当您遇到img元素时,将收集的文本添加到作为克隆子节点的文本节点。附加img。再次开始收集文本 继续,直到结束,然后用克隆替换文档中的原始根元素 编辑 比如:
function toArray(o) {
var a = [], i = o.length;
while (i--) {
a[i] = o[i];
}
return a;
}
function cleanUp(el) {
var e = el.cloneNode(false);
function addText(text) {
if (text != '') {
e.appendChild(document.createTextNode(text));
}
}
function collectText(el) {
var node, nodes = toArray(el.childNodes);
var text = '';
for (var i=0, iLen=nodes.length; i<iLen; i++) {
node = nodes[i];
if (node.tagName && node.tagName.toLowerCase() == 'img') {
addText(text);
e.appendChild(node);
text = '';
} else if (node.nodeType == 3) {
text += node.data;
} else if (node.nodeType == 1) {
addText(text);
text = '';
collectText(node);
}
}
if (text != '') {
e.appendChild(document.createTextNode(text));
}
}
collectText(el);
el.parentNode.replaceChild(e, el);
}
函数到阵列(o){
变量a=[],i=o.length;
而(我--){
a[i]=o[i];
}
返回a;
}
函数清理(el){
变量e=el.cloneNode(假);
函数addText(文本){
如果(文本!=''){
e、 appendChild(document.createTextNode(text));
}
}
函数集合文本(el){
变量节点,节点=toArray(el.childNodes);
var text='';
对于(var i=0,iLen=nodes.length;i创建根元素的浅克隆。沿着原始元素树走,收集文本。当遇到img元素时,将收集的文本添加到作为克隆子元素的文本节点。追加img。再次开始收集文本
继续,直到结束,然后用克隆替换文档中的原始根元素
编辑
比如:
function toArray(o) {
var a = [], i = o.length;
while (i--) {
a[i] = o[i];
}
return a;
}
function cleanUp(el) {
var e = el.cloneNode(false);
function addText(text) {
if (text != '') {
e.appendChild(document.createTextNode(text));
}
}
function collectText(el) {
var node, nodes = toArray(el.childNodes);
var text = '';
for (var i=0, iLen=nodes.length; i<iLen; i++) {
node = nodes[i];
if (node.tagName && node.tagName.toLowerCase() == 'img') {
addText(text);
e.appendChild(node);
text = '';
} else if (node.nodeType == 3) {
text += node.data;
} else if (node.nodeType == 1) {
addText(text);
text = '';
collectText(node);
}
}
if (text != '') {
e.appendChild(document.createTextNode(text));
}
}
collectText(el);
el.parentNode.replaceChild(e, el);
}
函数到阵列(o){
变量a=[],i=o.length;
而(我--){
a[i]=o[i];
}
返回a;
}
函数清理(el){
变量e=el.cloneNode(假);
函数addText(文本){
如果(文本!=''){
e、 appendChild(document.createTextNode(text));
}
}
函数集合文本(el){
变量节点,节点=toArray(el.childNodes);
var text='';
for(var i=0,iLen=nodes.length;i执行DOM替换几乎肯定也会丢失光标位置/选择,但仍然是正确的方法。我建议使用for和跨浏览器范围/选择处理(披露:我是Rangy的作者)
下面是一个示例,它删除了非
元素,并在所有主要浏览器(包括IE 6)中保留了以前的选择/插入符号位置。它递归地将主容器节点的
和文本子体移动到DocumentFragment
中,并在最后将片段附加到现在为空的容器节点之前删除所有其他节点。它还规范化(即连接相邻的文本节点)
带Rangy选择的JSFIDLE保存和恢复:
元件移除代码:
function removeNonImgElements(node) {
var frag = document.createDocumentFragment();
function move(node, moveSelf) {
var type = node.nodeType, name = node.nodeName;
// Deal with child nodes first
var child;
while ( (child = node.firstChild) ) {
move(child, true);
}
if (!moveSelf) {
return;
}
// Keep text, images and Rangy selection marker elements
if (type == 1 && (name == "IMG" ||
(name == "SPAN" && /^selectionBoundary/.test(node.id)))) {
frag.appendChild(node);
} else if (type == 3) {
var previousNode = frag.lastChild;
if (previousNode && previousNode.nodeType == 3) {
// Concatenate text nodes rather than have two adjacent
previousNode.data = previousNode.data + node.data;
node.parentNode.removeChild(node);
} else {
frag.appendChild(node);
}
} else {
node.parentNode.removeChild(node);
}
}
move(node, false);
node.appendChild(frag);
}
进行DOM替换几乎肯定也会丢失光标位置/选择,但仍然是正确的方法。我建议使用,以及跨浏览器范围/选择处理(披露:我是Rangy的作者)
下面是一个示例,它删除了非
元素,并在所有主要浏览器(包括IE 6)中保留了以前的选择/插入符号位置。它递归地将主容器节点的
和文本子体移动到DocumentFragment
中,并在最后将片段附加到现在为空的容器节点之前删除所有其他节点。它还规范化(即连接相邻的文本节点)
带Rangy选择的JSFIDLE保存和恢复:
元件移除代码:
function removeNonImgElements(node) {
var frag = document.createDocumentFragment();
function move(node, moveSelf) {
var type = node.nodeType, name = node.nodeName;
// Deal with child nodes first
var child;
while ( (child = node.firstChild) ) {
move(child, true);
}
if (!moveSelf) {
return;
}
// Keep text, images and Rangy selection marker elements
if (type == 1 && (name == "IMG" ||
(name == "SPAN" && /^selectionBoundary/.test(node.id)))) {
frag.appendChild(node);
} else if (type == 3) {
var previousNode = frag.lastChild;
if (previousNode && previousNode.nodeType == 3) {
// Concatenate text nodes rather than have two adjacent
previousNode.data = previousNode.data + node.data;
node.parentNode.removeChild(node);
} else {
frag.appendChild(node);
}
} else {
node.parentNode.removeChild(node);
}
}
move(node, false);
node.appendChild(frag);
}
仍然在after()调用中使用innerHTML
。仍然在after()调用中使用innerHTML
call。我不会为hasChildNodes测试操心,因为你正在删除节点,你可以在有第一个孩子的时候继续进行。连接相邻的文本节点是个好主意。@RobG:是的,我同意你的意见。我不确定为什么要把它放进去;我通常不会操心。我会删除它。我做了这个,但最终使用了Rob的解决方案,bec因为添加Rangy对我来说有点太重了(不可否认,如果我一直这么说,并最终有数千行代码来破解问题,我可能应该重新评估——但我们还没有达到这一点。在我们的应用程序中,我们正在对粘贴/keydown/mouseup/mousedown进行清理。这意味着焦点应该始终位于被替换内容的最后一个节点之后,我只是通过硬编码一个使用setStartAfter
到窗口进行范围设置。在这样的清理之后,getSelection
似乎在我的测试中效果很好。@Gijs:这很公平。Rangy的主要使用案例是如果您支持IE<9,我假设如果您使用的是范围和窗口。getSelection
,那么Rangy在上面的函数是最小的,很容易删除,但是@RobG的解决方案也一样好。我不想麻烦hasChildNodes测试,因为你要删除节点,你可以在有第一个孩子的时候继续进行。连接相邻的文本节点是个好主意。@RobG:是的,我同意你的意见。我不确定为什么我会把它放进去;我通常不会麻烦。我将删除它。我+了这个,但最终还是使用了Rob的解决方案,因为添加Rangy对于我正在做的事情来说有点太过繁重了(不可否认,如果我一直这么说,并且最终有数千行代码需要修改的话)