javascript-从contentEditable di提取文本

我将div设置为createTextNode,并将其样式设置为“ contentEditable”,以便保留换行符之类的内容。 在Safari,FF和IE中,div的外观和工作原理几乎相同。 一切都很好。 我想做的是从该div中提取文本,但要以不会丢失格式的方式-具体来说,就是换行。

我们使用的是jQuery,它的createTextNode函数基本上执行了预定的DFS,并将DOM的该分支中的所有内容粘合在一起。 这将丢失格式。

我看了看createTextNode函数,但是似乎所有这三种浏览器都使用contentEditable div幕后生成的实际HTML做不同的事情。 假设我在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将具有使用CSS完成的动态设置样式(字体,颜色,大小和对齐方式),因此我不确定是否可以使用createTextNode标记(在使用Google找到的某些页面上已经提到过)。

有谁知道任何JavaScript代码和/或jQuery插件,或将要从contentEditable div中提取文本以保留换行符的内容吗? 如果不需要,我宁愿不要重新发明解析轮。

更新:我从jQuery 1.4.2抄写了createTextNode函数,并对其进行了修改,以使用几乎完整的空格将其提取(我只选择了一行添加了换行符);

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的实现可能会混用空白。 这是真的还是我做错了什么?

6个解决方案
36 votes

不幸的是,您仍然必须针对每个浏览器分别处理$("#edit").getPreText()案例(在很多情况下,我不容忍浏览器检测,使用功能检测...但是在这种情况下是有必要的),但是幸运的是您可以照顾好他们 一切都非常简洁,像这样:

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();

您可以在这里进行测试。 IE尤其麻烦,因为$("#edit").getPreText()和文本转换中的新行是这样做的原因,这就是为什么它需要上面的contentEditable处理才能使其一致,因此它需要2遍才能正确处理。

在上面的$("#edit").getPreText()中,是contentEditable组件的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();
}

您可以在这里进行测试。 或者,由于无论如何都是基于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()进行调用,您可以在此处测试该版本。

Nick Craver answered 2020-06-29T18:18:24Z
4 votes

直到现在,当Nico对它打赏金时,我都忘记了这个问题。

我通过编写自己需要的功能,从现有的jQuery代码库编写功能并将其修改为可以根据需要工作的方式解决了该问题。

我已经使用Safari(WebKit),IE,Firefox和Opera对该功能进行了测试。 因为整个contentEditable都是非标准的,所以我没有费心检查其他任何浏览器。 如果任何浏览器的更新更改了实现contentEditable的方式,也可能会破坏此功能。 所以程序员要当心。

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;
}
Shaggy Frog answered 2020-06-29T18:18:53Z
1 votes

看到这个小提琴

还是这个帖子

如何解析具有浏览器兼容性的可编辑DIV文本

经过大量的努力...........创建的

user10 answered 2020-06-29T18:19:26Z
1 votes

我今天在Firefox中发现了这一点:

我将一个可编辑的div传递给该函数,该div的空白设置为“ pre”,并且效果很好。

我添加了一行以显示有多少个节点,并添加了一个将输出放入另一个PRE的按钮,以证明换行符是完整的。

它基本上是这样说的:

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.

有一个问题。 当您在原始文本的任何行的结尾处按Enter键时,而不是在其中插入LF,而是在其中插入“”。您可以再次按Enter键,并将LF在其中输入,但不是第一次。 并且您必须删除“”(它看起来像一个空格)。 去吧-我想那是一个错误。

在IE8中不会发生这种情况。 (将textContent更改为innerText)。 当您按下Enter键时,就像在Firefox中一样,它会将节点分为2个节点,但是每个节点中的每个节点的“数据”属性都变为“未定义”。

我敢肯定,这里正在发生的事情远远超出了人们的视线,因此,对此事的任何投入都会令人启发。

<!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>
alfadog67 answered 2020-06-29T18:20:13Z
0 votes

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

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

请在此处查看测试页:[http://brokendisk.com/code/contenteditable.html]

尽管我认为真正的答案是,如果您对浏览器提供的标记不感兴趣,则不应该使用contenteditable属性-textarea将是完成此任务的合适工具。

Jon z answered 2020-06-29T18:20:42Z
-1 votes
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;
}
Artur Vanesyan answered 2020-06-29T18:20:57Z
translate from https://stackoverflow.com:/questions/3455931/extracting-text-from-a-contenteditable-div