从contentEditable div中提取文本

我有一个div设置为contentEditable和“ white-space:pre ”样式,所以它保持像linebreaks的东西。 在Safari,FF和IE中,div看起来很像,工作原理也一样。 一切都很好。 我想要做的是从这个div中提取文本,但以这种方式不会丢失格式 – 特别是换行符。

我们正在使用jQuery,其text()函数基本上是一个预定义的DFS,并将DOM分支中的所有内容粘合到一起。 这会丢失格式。

我看了一下html()函数,但是似乎所有三个浏览器在我的contentEditable div中都会在幕后生成实际的HTML。 假设我把这个input到我的div中:

 1 2 3 

这是结果:

Safari 4:

 1 <div>2</div> <div>3</div> 

Firefox 3.6:

 1 <br _moz_dirty=""> 2 <br _moz_dirty=""> 3 <br _moz_dirty=""> <br _moz_dirty="" type="_moz"> 

IE 8:

 <P>1</P><P>2</P><P>3</P> 

啊。 这里没什么很一致的。 令人惊讶的是,MSIE看起来是最健康的! (大写P标签和所有)

该div将dynamic设置样式(字体,颜色,大小和alignment),这是使用CSS完成的,所以我不确定我是否可以使用pre标记(在我使用Google发现的某些页面上提到过)。

有谁知道任何JavaScript代码和/或jQuery插件或从contentEditable div中提取文本的方式,以保留换行符? 如果我不需要,我宁愿不重新创build一个parsing轮。

更新:我从jQuery 1.4.2中删除了getText函数,并修改了它的空白大部分完好无损(我只chnaged一行,我添加一个换行符);

 function extractTextWithWhitespace( elems ) { var ret = "", elem; for ( var i = 0; elems[i]; i++ ) { elem = elems[i]; // Get the text from text nodes and CDATA nodes if ( elem.nodeType === 3 || elem.nodeType === 4 ) { ret += elem.nodeValue + "\n"; // Traverse everything else, except comment nodes } else if ( elem.nodeType !== 8 ) { ret += extractTextWithWhitespace2( elem.childNodes ); } } return ret; } 

我调用这个函数并使用它的输出将它分配给一个带有jQuery的XML节点,如下所示:

 var extractedText = extractTextWithWhitespace($(this)); var $someXmlNode = $('<someXmlNode/>'); $someXmlNode.text(extractedText); 

最终得到的XML最终通过AJAX调用发送到服务器。

这在Safari和Firefox中运行良好。

在IE上,只有第一个'\ n'似乎得到了保留。 更多的看,它看起来像jQuery设置文本像这样(jQuery-1.4.2.js行4004):

 return this.empty().append( (this[0] && this[0].ownerDocument || document).createTextNode( text ) ); 

阅读createTextNode ,似乎IE的实现可能会混搭空白。 这是真的还是我做错了什么?

不幸的是,你仍然需要为每个浏览器单独处理这个情况(我不会容忍浏览器检测,在许多情况下,使用function检测…但在这种情况下,这是必要的),但幸运的是,你可以照顾他们一切都很简洁,就像这样:

 var ce = $("<pre />").html($("#edit").html()); if($.browser.webkit) ce.find("div").replaceWith(function() { return "\n" + this.innerHTML; }); if($.browser.msie) ce.find("p").replaceWith(function() { return this.innerHTML + "<br>"; }); if($.browser.mozilla || $.browser.opera ||$.browser.msie ) ce.find("br").replaceWith("\n"); var textWithWhiteSpaceIntact = ce.text(); 

你可以在这里testing一下 。 特别是IE浏览器是一件麻烦的事&nbsp; 并在文本转换新行,这就是为什么它得到了以上的处理,以使其一致,所以它需要2通过正确处理。

在上面#editcontentEditable组件的ID,所以只需要改变它,或者把它作为一个函数,例如:

 function getContentEditableText(id) { var ce = $("<pre />").html($("#" + id).html()); if ($.browser.webkit) ce.find("div").replaceWith(function() { return "\n" + this.innerHTML; }); if ($.browser.msie) ce.find("p").replaceWith(function() { return this.innerHTML + "<br>"; }); if ($.browser.mozilla || $.browser.opera || $.browser.msie) ce.find("br").replaceWith("\n"); return ce.text(); } 

你可以在这里testing 。 或者,因为这是build立在jQuery方法上,所以把它作为一个插件,就像这样:

 $.fn.getPreText = function () { var ce = $("<pre />").html(this.html()); if ($.browser.webkit) ce.find("div").replaceWith(function() { return "\n" + this.innerHTML; }); if ($.browser.msie) ce.find("p").replaceWith(function() { return this.innerHTML + "<br>"; }); if ($.browser.mozilla || $.browser.opera || $.browser.msie) ce.find("br").replaceWith("\n"); return ce.text(); }; 

然后你可以用$("#edit").getPreText()来调用它, 你可以在这里testing这个版本 。

到现在为止,我忘记了这个问题,尼科打了一个奖金。

我通过编写我自己需要的函数解决了这个问题,从现有的jQuery代码库中挑选一个函数,并根据需要修改它。

我已经用Safari(WebKit),IE,Firefox和Operatesting了这个function。 我没有打扰检查任何其他浏览器,因为整个内容可编辑的事情是非标准的。 如果任何浏览器的更新可能会改变如何实现contentEditable,则可能会中断此function。 所以程序员要小心。

 function extractTextWithWhitespace(elems) { var lineBreakNodeName = "BR"; // Use <br> as a default if ($.browser.webkit) { lineBreakNodeName = "DIV"; } else if ($.browser.msie) { lineBreakNodeName = "P"; } else if ($.browser.mozilla) { lineBreakNodeName = "BR"; } else if ($.browser.opera) { lineBreakNodeName = "P"; } var extractedText = extractTextWithWhitespaceWorker(elems, lineBreakNodeName); return extractedText; } // Cribbed from jQuery 1.4.2 (getText) and modified to retain whitespace function extractTextWithWhitespaceWorker(elems, lineBreakNodeName) { var ret = ""; var elem; for (var i = 0; elems[i]; i++) { elem = elems[i]; if (elem.nodeType === 3 // text node || elem.nodeType === 4) // CDATA node { ret += elem.nodeValue; } if (elem.nodeName === lineBreakNodeName) { ret += "\n"; } if (elem.nodeType !== 8) // comment node { ret += extractTextWithWhitespace(elem.childNodes, lineBreakNodeName); } } return ret; } 

看到这个小提琴

或者这个职位

如何用浏览器兼容性parsing可编辑的DIV文本

创造了很多努力………..

我今天在Firefox中发现了这个:

我把这个function设置为“pre”的contenteditable div传递给它,并且它的function非常强大。

我添加了一行来显示有多less个节点,还有一个将输出放到另一个PRE中的button,只是为了certificate换行符是完整的。

它基本上这样说:

 For each child node of the DIV, if it contains the 'data' property, add the data value to the output otherwise add an LF (or a CRLF for Windows) } and return the result. 

有一个问题, 当你在原文的任何一行的末尾按回车键,而不是放入一个LF,它会放入一个“”。你可以再次点击进入,并在那里放入一个LF,但不是第一次。 你必须删除“”(看起来像一个空格)。 去图 – 我想这是一个错误。

这在IE8中不会发生。 (将textContent更改为innerText)这里有一个不同的bug,tho。 当你敲回车键时,它将把节点分成两个节点,就像在Firefox中一样,但是每个节点的“data”属性都变成了“undefined”。

我敢肯定还有更多的事情要做,所以对这件事的任何投入都是有启发性的。

 <!DOCTYPE html> <html> <HEAD> <SCRIPT type="text/javascript"> function htmlToText(elem) { var outText=""; for(var x=0; x<elem.childNodes.length; x++){ if(elem.childNodes[x].data){ outText+=elem.childNodes[x].data; }else{ outText+="\n"; } } alert(elem.childNodes.length + " Nodes: \r\n\r\n" + outText); return(outText); } </SCRIPT> </HEAD> <body> <div style="white-space:pre;" contenteditable=true id=test>Text in a pre element is displayed in a fixed-width font, and it preserves both spaces and line breaks </DIV> <INPUT type=button value="submit" onclick="document.getElementById('test2').textContent=htmlToText(document.getElementById('test'))"> <PRE id=test2> </PRE> </body> </html> 

下面是一个解决scheme(使用下划线和jQuery),似乎可以在OS X中的iOS Safari(iOS 7和8),Safari 8,Chrome 43和Firefox 36以及Windows上的IE6-11中使用:

 _.reduce($editable.contents(), function(text, node) { return text + (node.nodeValue || '\n' + (_.isString(node.textContent) ? node.textContent : node.innerHTML)); }, '') 

在这里看到testing页面: http : //brokendisk.com/code/contenteditable.html

尽pipe我认为真正的答案是,如果您对浏览器提供的标记不感兴趣,则不应该使用contenteditable属性 – textarea将是该作业的正确工具。

 this.editableVal = function(cont, opts) { if (!cont) return ''; var el = cont.firstChild; var v = ''; var contTag = new RegExp('^(DIV|P|LI|OL|TR|TD|BLOCKQUOTE)$'); while (el) { switch (el.nodeType) { case 3: var str = el.data.replace(/^\n|\n$/g, ' ').replace(/[\n\xa0]/g, ' ').replace(/[ ]+/g, ' '); v += str; break; case 1: var str = this.editableVal(el); if (el.tagName && el.tagName.match(contTag) && str) { if (str.substr(-1) != '\n') { str += '\n'; } var prev = el.previousSibling; while (prev && prev.nodeType == 3 && PHP.trim(prev.nodeValue) == '') { prev = prev.previousSibling; } if (prev && !(prev.tagName && (prev.tagName.match(contTag) || prev.tagName == 'BR'))) { str = '\n' + str; } }else if (el.tagName == 'BR') { str += '\n'; } v += str; break; } el = el.nextSibling; } return v; }