Подтвердить что ты не робот

Кросс-браузерный способ вставки ярлыка BR или P при нажатии Enter на элементе contentEditable

Когда вы нажимаете Enter на элементе contentEditable, каждый браузер обрабатывает полученный код по-разному: Firefox вставляет ярлык BR, Chrome вставляет тег DIV, в то время как Internet Explorer вставляет тег..

Я отчаянно искал решение, по крайней мере, использовать BR или P для всех браузеров, и наиболее распространенным ответом было следующее:

вставка тега BR:

$("#editableElement").on("keypress", function(e){
      if (e.which == 13) {
        if (window.getSelection) {
          var selection = window.getSelection(),
              range = selection.getRangeAt(0),
              br = document.createElement("br");
          range.deleteContents();
          range.insertNode(br);
          range.setStartAfter(br);
          range.setEndAfter(br);
          selection.removeAllRanges();
          selection.addRange(range);
          return false;
        }
      }
    });

Но это не работает, потому что кажется, что браузеры не знают, как установить каретку после <br>, а это значит, что следующее не делает ничего полезного (особенно если вы нажмете enter, когда каретка помещается в конце текста):

range.setStartAfter(br);
range.setEndAfter(br);

Некоторые люди скажут: используйте double <br><br>, но это приводит к разрыву в двух строках, когда вы нажимаете enter внутри текста node.

Другие скажут, что всегда добавить дополнительный <br> в конец contentEditable, но если у вас есть <div contenteditable><p>text here</p></div>, и вы поместите курсор в конец текста, тогда нажмите enter, вы получите неправильное поведение.

Поэтому я сказал себе, может быть, мы сможем использовать P вместо BR, а общий ответ:

вставка тега P:

document.execCommand('formatBlock', false, 'p');

Но это тоже не работает.

Как вы можете видеть, все эти решения оставляют желать лучшего. Есть ли другое решение, которое решает эту проблему?

4b9b3361

Ответ 1

Одно из возможных решений: добавьте текст node с символом нулевой ширины после элемента <br>. Это непечатаемый символ нулевой ширины, специально предназначенный для:

... указать границы слов в системах обработки текста при использовании скриптов которые не используют явный интервал, или после символов (например, слэш), за которым не следует видимое пространство, но после которого тем не менее, может быть разрыв строки.

(Википедия)

Протестировано в Chrome 48, Firefox 43 и IE11.

$("#editableElement").on("keypress", function(e) {
  //if the last character is a zero-width space, remove it
  var contentEditableHTML = $("#editableElement").html();
  var lastCharCode = contentEditableHTML.charCodeAt(contentEditableHTML.length - 1);
  if (lastCharCode == 8203) {
    $("#editableElement").html(contentEditableHTML.slice(0, -1));
  }
  // handle "Enter" keypress
  if (e.which == 13) {
    if (window.getSelection) {
      var selection = window.getSelection();
      var range = selection.getRangeAt(0);
      var br = document.createElement("br");
      var zwsp = document.createTextNode("\u200B");
      var textNodeParent = document.getSelection().anchorNode.parentNode;
      var inSpan = textNodeParent.nodeName == "SPAN";
      var span = document.createElement("span");
      
      // if the carat is inside a <span>, move it out of the <span> tag
      if (inSpan) {
        range.setStartAfter(textNodeParent);
        range.setEndAfter(textNodeParent);
      }

      // insert the <br>
      range.deleteContents();
      range.insertNode(br);
      range.setStartAfter(br);
      range.setEndAfter(br);
      
      // create a new span on the next line
      if (inSpan) {
        range.insertNode(span);
        range.setStart(span, 0);
        range.setEnd(span, 0);
      }

      // add a zero-width character
      range.insertNode(zwsp);
      range.setStartBefore(zwsp);
      range.setEndBefore(zwsp);
      
      // insert the new range
      selection.removeAllRanges();
      selection.addRange(range);
      return false;
    }
  }
});
#editableElement {
  height: 150px;
  width: 500px;
  border: 1px solid black;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div contenteditable=true id="editableElement">
  <span>sample text</span>
</div>

Ответ 2

Вы можете увидеть полную реализацию перекрестного браузера здесь. Есть так много хаков, чтобы заставить его работать. Этот код из ссылки поможет вам настроить решение.

Пример взлома Geko и IE:

doc.createElement( 'br' ).insertAfter( startBlock );

// A text node is required by Gecko only to make the cursor blink.
if ( CKEDITOR.env.gecko )
    doc.createText( '' ).insertAfter( startBlock );

// IE has different behaviors regarding position.
range.setStartAt( startBlock.getNext(), 
    CKEDITOR.env.ie ? CKEDITOR.POSITION_BEFORE_START :
        CKEDITOR.POSITION_AFTER_START );