我将div设置为contentEditable
并使用“white-space:pre
”设置样式,因此它可以保留换行符。在Safari,FF和IE中,div几乎看起来和工作原理相同。一切都很好。我想要做的是从这个div中提取文本,但是这样的方式不会丢失格式 - 特别是换行符。
我们正在使用jQuery,其text()
函数基本上执行预订DFS并将DOM的该分支中的所有内容粘合到一起。这会丢失格式。
我查看了html()
函数,但似乎所有三个浏览器都在我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完成的,因此我不确定是否可以使用pre
标记(在某些标记中提到)我发现使用Google的网页。
有没有人知道任何JavaScript代码和/或jQuery插件或者什么东西会从contentEditable div中提取文本以保留换行符?我宁愿不重新发明解析如果我不需要轮子。
更新:我在jQuery 1.4.2中修改了getText
函数并对其进行了修改以提取它,其中大部分空白完整(我只添加了一行,我添加了换行符);
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的实现可能会混淆空白。这是真的还是我做错了什么?
答案 0 :(得分:36)
不幸的是,你仍然必须为每个浏览器单独处理pre
个案(在许多情况下我不宽恕浏览器检测,使用功能检测......但在这种情况下它是必要的),但幸运的是你可以非常简洁地照顾它们,如下所示:
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();
You can test it out here。特别是IE是一个麻烦,因为
的方式和文本转换中的新行,这就是为什么它获得上面的<br>
处理使其一致,所以它需要2次传递才能正确处理
在上面#edit
是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();
}
You can test that here。或者,因为这是基于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()
,you can test that version here来调用它。
答案 1 :(得分:4)
到目前为止,我已经忘记了这个问题,当时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;
}
答案 2 :(得分:1)
答案 3 :(得分:1)
我今天在Firefox中发现了这个:
我传递了一个满足的div,他的白色空间被设置为“预先”到这个功能,并且它工作得很厉害。
我添加了一行来显示有多少节点,还有一个按钮将输出放到另一个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.
有一个问题,那么。当你在原始文本的任何一行的末尾点击输入时,不是输入一个LF,而是输入一个“”。你可以再次点击输入并在那里输入一个LF,但不是第一次。你必须删除“”(它看起来像一个空格)。去图 - 我猜这是一个错误。
这在IE8中不会发生。 (将textContent更改为innerText)那里有一个不同的错误。当你按Enter键时,它会将节点拆分为2个节点,就像在Firefox中一样,但是每个节点的“data”属性都会变为“未定义”。
我确信这里有更多的事情要比满足眼睛,所以对此事的任何意见都会有所启发。
<!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>
答案 4 :(得分:0)
这是一个解决方案(使用下划线和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));
}, '')
请在此处查看测试页:http://brokendisk.com/code/contenteditable.html
虽然我认为真正的答案是如果你对浏览器提供的标记不感兴趣,你不应该使用contenteditable
属性 - textarea将是适合这项工作的工具。
答案 5 :(得分:-1)
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;
}