如何在 Firefox ContentEditable 中强制 <br> 换行

How to force <br> line break in Firefox ContentEditable

提问人:Pekka 提问时间:10/26/2011 最后编辑:Pekka 更新时间:5/2/2018 访问量:10188

问:

我有一个 JavaScript 所见即所得的编辑器(与 CKEditor 不同)在站点上运行。

它有一个设置,使IE在编辑器中按下时创建换行符。<br>Enter

这很好用,但不幸的是,Firefox(我已经用 5 和 7 进行了测试)仍然会生成元素,并且只有在您使用 .<p><br>Shift + Enter

有没有办法让Firefox总是在contentEditable中生成元素?<br>

HTML Firefox 所见即所得

评论

0赞 Pekka 10/26/2011
奇怪的是,有一个 SO 问题抱怨完全相反: contentEditable - Firefox <br /> 标签 但在 FF5 和 FF7 中,我很肯定我看到正在使用标签<p>
0赞 Šime Vidas 10/31/2011
JavaScript 解决方案可以接受吗?
0赞 Šime Vidas 10/31/2011
在我的Firefox(Win7上的7.0.1)中,当我在这个DIV中按ENTER键时,插入了一个元素。<br _moz_dirty="">
3赞 Šime Vidas 10/31/2011
我在其他浏览器中的经验:最新的 Chrome 插入元素,IE9 和 Opera 插入元素。由于不同浏览器的行为不一致,因此如果按下 ENTER,我建议您只在处理程序中,然后手动插入 或 .<div><p>e.preventDefault()keypress<br>\n

答:

17赞 LoveAndCoding 10/31/2011 #1

从标准来看,这个动作似乎是应该处理的方式。当没有在回车键上设置修饰符时,块被破坏,当按下 shift 键时,块被破坏。[来源].<br>

也就是说,这并不能解决您的问题,所以让我们继续努力。这条线应该做

document.execCommand('insertBrOnReturn', false, true);

设置它,以便当您刚刚点击回车时,它只放入一个标签。这仅受 FF 支持,因此它不会影响 IE 上的任何内容。不过不了解其他浏览器。<br>

注意:如果用户第二次点击,而不键入任何内容,则无论如何都会创建一个新的段落标记。为了防止这种情况,您可以捕获 using 事件并停止它,或者您可以在继续事件之前插入一个(我建议这样做)。EnterEnterkeypress&nbsp;

对于您来说,此注释的困难部分是检查 上的元素状态。懒惰的解决方案(除非不这样做很重要,否则我会推荐)是在每个键之前插入它。如果您想以另一种方式执行此操作,我会检查元素并查看最后几个字符(不修剪内容)是否(因此可能正则表达式匹配)。keypressEnter.innerHTML<br/>/\<br\s?\/?\>$/

评论

0赞 Pekka 10/31/2011
非常有趣 - 我很快就会测试这个。
0赞 Pekka 11/7/2011
这最直接地解决了我眼前的问题,所以把赏金奖励给你。谢谢!
0赞 Sascha 7/12/2018
我很高兴看到一个 7 年前的答案,它实际上解决了我的问题。谢谢!
9赞 Šime Vidas 10/31/2011 #2

考虑一下:

HTML格式:

<div contentEditable id="input"></div>

CSS:

#input {
    border: 3px solid #07c;
    width: 300px;
    height: 200px;
    white-space: pre;
}

JavaScript的:

$( input ).keypress( function ( e ) {
    var sel, node, offset, text, textBefore, textAfter, range;

    sel = window.getSelection();

    // the node that contains the caret
    node = sel.anchorNode;

    // if ENTER was pressed while the caret was inside the input field
    if ( node.parentNode === input && e.keyCode === 13 ) {

        // prevent the browsers from inserting <div>, <p>, or <br> on their own
        e.preventDefault();

        // the caret position inside the node
        offset = sel.anchorOffset;        

        // insert a '\n' character at that position
        text = node.textContent;
        textBefore = text.slice( 0, offset );
        textAfter = text.slice( offset ) || ' ';
        node.textContent = textBefore + '\n' + textAfter;

        // position the caret after that new-line character
        range = document.createRange();
        range.setStart( node, offset + 1 );
        range.setEnd( node, offset + 1 );

        // update the selection
        sel.removeAllRanges();
        sel.addRange( range );
    }
});

现场演示:http://jsfiddle.net/FhEf6/3/

我使用字符而不是 BR 元素(DIV 已设置)。因此,当按下 ENTER 时,不会向 DIV 添加 BR 或 P 元素。DIV 内部始终只有一个 TextNode,所有换行符都由字符表示。'\n'white-space:pre'\n'

评论

0赞 Pekka 10/31/2011
看起来很有趣 - 我会尽快测试这个。
0赞 scorp 2/6/2014 #3

不完全是你问的,但当你想强制时,这个函数可能会很有趣 其他浏览器 opera、chrome、internet explorer 在回车时插入常规 BR。 可以写得更好,但到目前为止有效

enterBR = function(e)
{   
    var iframeElement=parent.window.$("wysiwyg"+n);
    e = e || iframeElement.contentWindow.event;                 // eventhandler inside iframe (!)
    var keyCode= e.keyCode? e.keyCode: e.which;                 // req. for IE below v8

if (keyCode==13)                                // if enter key gets pressed
{ 
     if(IE)      // internet explorer
     { e.preventDefault?e.preventDefault():e.returnValue=false; // block default handling ( i tell yo!)
       iframeElement.contentDocument.selection.createRange().pasteHTML("<br/>");  // insert a br tag
     }

     if(OP||CR)  // opera and chrome
     { 
      iframeElement.contentWindow.document.execCommand('formatBlock',false,'p');
     } // creates a paragraph around a br tag <p><br/></p> replace with regExp

    //here the regExp to replace the unneeded paragraph tags and a detection if you have whatever xhtml compatible <br/>'s or not.
    HTML=iframeElement.contentWindow.document.body.innerHTML;
    HTML=HTML.replace(/<p><br([\/]?)><\/p>/gi,'<br/>');
    HTML=HTML.replace(/<br(.*?|\s*[^\/]+[^>]?)([\/]?)>/mgi,"<br$1/>\n"); 

}
  return false;
}

// finaly add an eventlistener. remember this is used inside an iframe so dont forget to advise the script to work from one dom layer above (parent.window)
// but i think it can be easily rewritten for contenteditable div or something...
addEventListener(parent.window.$("mywysiwygeditor").contentDocument,'keypress',enterBR);

高频

0赞 Imanuel Habekotte 5/2/2018 #4

我的解决方案在Firefox中完美运行,尽管它出乎意料!我还在 Edge 和 Chrome 中测试了它。它基于 @Ktash 的答案,将 nbsp 替换为三个消失的节点:一个空的 span(带有一个类)、中间的 a 和一个包含 .<br>&nbsp;

JS:

var isFirefox = navigator.userAgent.toLowerCase().indexOf('firefox') > -1;
var isChrome = navigator.userAgent.toLowerCase().indexOf('chrome') > -1;

document.execCommand('insertBrOnReturn', false, true);

var elements = document.getElementsByClassName('editable');
Array.from(elements).forEach( (el) => {
    el.addEventListener('keydown',function(e) {
        if (e.which == 13) {
            if (isFirefox) {
                document.execCommand('insertHTML', false, '<span class="nbsp-break"></span><br><span>&nbsp;</span>');
                e.preventDefault();
            } else if (!isChrome) {
                document.execCommand('insertText', true, '\r\n');
                document.execCommand('insertHTML', false, "<br>");
                e.preventDefault();
            }
        }
    });

    if (isFirefox) {
        el.addEventListener('input',function(e) {
            var elements = document.getElementsByClassName('nbsp-break');
            Array.from(elements).forEach( (el) => {
                el.parentNode.removeChild(el.nextSibling.nextSibling);
                el.parentNode.removeChild(el);
            });
        });
    }
});

CSS:

.nbsp-break + br + span {
    font-size: 0;
}