Home » Jquery » jquery – How to organize generated code and avoid copying format when copying and pasting text in the editor WYSIWYG?-Exceptionshub

jquery – How to organize generated code and avoid copying format when copying and pasting text in the editor WYSIWYG?-Exceptionshub

Posted by: admin February 24, 2020 Leave a comment

Questions:

I have observed some questions but all are based on the use of a plugin. But I have my own editor so the answers given do not solve my problem.

The issue is that in each tabulation that is given, the code is generated with a div container and, the paragraphs in br in such a way:

enter image description here

How to avoid the use of div y, the paragraphs in br, instead of them, use only p and in an organized way for example:

enter image description here

And finally

The issue I’ve got is that whenever you copy and paste something into the editor, it copies over the formatting from wherever you’ve copied it.

enter image description here

How can I correct these defects of my editor, here my complete code: https://jsfiddle.net/p87t0aqx/

How to&Answer:

This might not be a perfect solution, as there might be a plenty of work to be done to make the WYSIWYG editor truly usable.

Original Solution:

What the solution does:

  • Format the HTML code on Code Toggle before displaying.
  • Detect Enter Key and apply <p> format, otherwise apply <div> format.
  • On Clipboard Paste, intercept the default clipboard pasting behavior and then insert plain text at current editor’s cursor position.
$(function() {
  // https://stackoverflow.com/questions/26360414/javascript-how-to-correct-indentation-in-html-string
  function formatHtmlCode(str) {

    var div = document.createElement('div');
    div.innerHTML = str.trim();

    return format(div, 0).innerHTML;
  }

  function format(node, level) {
    var indentBefore = new Array(level++ + 1).join('  '),
      indentAfter = new Array(level - 1).join('  '),
      textNode;

    for (var i = 0; i < node.children.length; i++) {

      textNode = document.createTextNode('\n' + indentBefore);
      node.insertBefore(textNode, node.children[i]);

      format(node.children[i], level);

      if (node.lastElementChild == node.children[i]) {
        textNode = document.createTextNode('\n' + indentAfter);
        node.appendChild(textNode);
      }
    }

    return node;
  }

  $('#editControls a').click(function(e) {
    switch ($(this).data('role')) {
      case 'p':
        document.execCommand('formatBlock', false, $(this).data('role'));
        break;
        //Specific control for the code button.
      case 'code':
        //enable / disable code mode
        codeMode = !codeMode;
        if (codeMode) {
          // ON: show the code in text mode
          var formattedHtml = formatHtmlCode(htmlDiv.html());

          htmlDiv.css("white-space", "pre");
          htmlDiv.text(formattedHtml);
          //
          var editor = $("#editor")
          editor.addClass("black-bg-colr codeMode")
        } else {
          // OFF: reinterpret the code
          htmlDiv.css("white-space", "normal");
          htmlDiv.html(htmlDiv.text().replace(/\r?\n|\r/g, ""));
          var editor = $("#editor")
          editor.removeClass("black-bg-colr codeMode")
        }
        break;
      default:
        document.execCommand($(this).data('role'), false, null);
        break;
    }
  });

  //code mode control
  let codeMode = false;
  let htmlDiv = $("#editor");

  htmlDiv.on('keyup', function(e) {
    if (!e.shiftKey && e.keyCode === 13) {
      document.execCommand('formatBlock', false, 'p');
    } else if (e.shiftKey) {
      document.execCommand('formatBlock', false, 'div');
    }
  });

  htmlDiv.on("paste", function(e) {
    e.preventDefault();

    var text = (e.originalEvent || e).clipboardData.getData('text/plain');

    document.execCommand('formatBlock', false, 'div');
    document.execCommand('insertText', false, text);
  });

  //input -> NOT keyup
  htmlDiv.on("input", function(e) {
    //htmlDiv.on("keyup", function(e) {
    $(".editor-preview").val(htmlDiv.html());
    //$("#textarea").html(htmlDiv.html());
    $(".editor-preview").keyup();
    //$(".editor-preview").html(htmlDiv.html());
  });
  //$("#textarea").html(htmlDiv.html());

  $('.editor-preview').keyup(function() {
    //var value = $(this).val();
    var contentAttr = $(this).attr('class');
    if (!codeMode) {
      var value = $(this).val();
      //$( '.' + contentAttr + '' ).html(value);
      $('.' + contentAttr).html(value);
    } else {
      //$( '.' + contentAttr + '' ).html(htmlDiv.text());
      $('.' + contentAttr).html(htmlDiv.text());
    }
  });
});
#editControls {
  overflow: auto;
  border-top: 1px solid transparent;
  border-left: 1px solid transparent;
  border-right: 1px solid transparent;
  border-color: silver;
  border-top-left-radius: 5px;
  border-top-right-radius: 5px;
  padding: .5em 1em .5em 1em;
  background-color: #fbfbfb;
  margin: 0 auto;
  width: 100%;
  /*90*/
}

#editor {
  resize: vertical;
  overflow: auto;
  border: 1px solid silver;
  border-bottom-right-radius: 5px;
  border-bottom-left-radius: 5px;
  min-height: 100px;
  padding: 1em;
  background: white;
  margin: 0 auto;
  width: 100%;
  /*90*/
}

#editor:focus {
  outline: none !important;
  box-shadow: inset 0 0 2px silver;
}

.codeMode {
  font-family: Courier New, Source Code Pro Light, Medium, Source Code Pro ExtraLight, Menlo, Consola, Monaco Linux, Consola Regular, Fira Code Regular, DejaVu Sans Mono;
  /*font-family: inherit;*/
  /*font-family: 'Courier New';*/
  border: 0px;
  font-style: inherit;
  font-variant: inherit;
  font-weight: inherit;
  font-stretch: inherit;
  line-height: inherit;
  vertical-align: baseline;
  box-sizing: inherit;
  color: #fff;
}

.black-bg-colr {
  background-color: #000 !important;
}

.btn-group {
  position: relative;
  display: inline-block;
  font-size: 0;
  vertical-align: middle;
  white-space: nowrap;
}

.btn-group+.btn-group {
  margin-left: 5px;
}

.btn-group a {
  text-decoration: none;
}

.btn-editor {
  height: 30px;
  display: inline-block;
  padding: 6px 12px;
  margin-bottom: 0;
  font-size: 11px;
  font-weight: normal;
  line-height: 1.42857143;
  text-align: center;
  white-space: nowrap;
  vertical-align: middle;
  -ms-touch-action: manipulation;
  touch-action: manipulation;
  cursor: pointer;
  -webkit-user-select: none;
  -moz-user-select: none;
  -ms-user-select: none;
  user-select: none;
  background-image: none;
  border-radius: 4px;
  border: 1px solid transparent;
  color: #333;
  background-color: #fff;
  border-color: #ccc;
}

.btn-group>.btn-editor:first-child {
  margin-left: 0;
  -webkit-border-top-left-radius: 4px;
  -moz-border-radius-topleft: 4px;
  border-top-left-radius: 4px;
  -webkit-border-bottom-left-radius: 4px;
  -moz-border-radius-bottomleft: 4px;
  border-bottom-left-radius: 4px;
}

.btn-group>.btn-editor+.btn-editor {
  margin-left: -1px !important;
  border-top-right-radius: 4px;
  border-bottom-right-radius: 4px;
}

.btn-not-space {
  position: relative;
  float: left;
  margin-left: 0 !important;
  border-radius: inherit;
  border: 1px solid transparent;
  border-color: #ccc;
}

.btn-editor.btn-not-space:hover {
  background-color: rgba(230, 230, 230, 0.32);
}

#preview {
  padding: 1em;
  margin: 0 auto;
  width: 97%;
  border-top: 1px dotted #c8ccd0;
  border-bottom: 1px dotted #c8ccd0;
  clear: both;
}
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>WYSIWYG</title>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
</head>

<body>
  <div class="editor-wrapper">
    <div id="editControls">
      <div class="btn-group">
        <a class="btn-editor btn-not-space" data-role="bold" data-ref="#"><b>Bold</b></a>
        <a class="btn-editor btn-not-space" data-role="italic" data-ref="#"><em>Italic</em></a>
        <a class="btn-editor btn-not-space" data-role="underline" data-ref="#"><u><b>U</b></u></a>
        <a class="btn-editor btn-not-space" data-role="strikeThrough" data-ref="#"><strike>abc</strike></a>
      </div>
      <div class="btn-group">
        <a class="btn-editor btn-not-space" data-role="code" data-ref="#"><code>&lt;/&gt;</code></a>
      </div>
    </div>
    <div id="editor" contenteditable></div>
    <textarea id="textarea" name="detail" class="editor-preview" style="display: none;"></textarea>
    <div id="preview" class="editor-preview"></div>
  </div>

</body>

</html>

With Ace Editor:

$(function() {
  var aceEditor = ace.edit("ace-editor");
  aceEditor.setTheme("ace/theme/eclipse");
  aceEditor.session.setMode("ace/mode/html");

  $('#editControls a').click(function(e) {
    switch ($(this).data('role')) {
      case 'p':
        document.execCommand('formatBlock', false, $(this).data('role'));
        break;
        //Specific control for the code button.
      case 'code':
        //enable / disable code mode
        codeMode = !codeMode;
        if (codeMode) {
          // ON: show the code in text mode
          var editor = $("#editor");
          var $aceEditor = $("#ace-editor");

          var formattedHtml = html_beautify(htmlDiv.html());

          aceEditor.setValue(formattedHtml, -1);

          editor.css("display", "none");
          $aceEditor.css("display", "block");

        } else {
          // OFF: reinterpret the code
          htmlDiv.html(htmlDiv.text().replace(/\r?\n|\r/g, ""));
          var editor = $("#editor");
          var $aceEditor = $("#ace-editor");

          editor.css("display", "block");
          $aceEditor.css("display", "none");

          htmlDiv.html(aceEditor.getValue().replace(/\r?\n|\r/g, ""));
        }
        break;
      default:
        document.execCommand($(this).data('role'), false, null);
        break;
    }
  });

  //code mode control
  let codeMode = false;
  let htmlDiv = $("#editor");

  htmlDiv.on('keyup', function(e) {
    if (!e.shiftKey && e.keyCode === 13) {
      document.execCommand('formatBlock', false, 'p');
    } else if (e.shiftKey) {
      document.execCommand('formatBlock', false, 'div');
    }
  });

  htmlDiv.on("paste", function(e) {
    e.preventDefault();

    var text = (e.originalEvent || e).clipboardData.getData('text/plain');

    document.execCommand('formatBlock', false, 'div');
    document.execCommand('insertText', false, text);
  });

  //input -> NOT keyup
  htmlDiv.on("input", function(e) {
    //htmlDiv.on("keyup", function(e) {
    $(".editor-preview").val(htmlDiv.html());
    //$("#textarea").html(htmlDiv.html());
    $(".editor-preview").keyup();
    //$(".editor-preview").html(htmlDiv.html());
  });
  //$("#textarea").html(htmlDiv.html());

  aceEditor.getSession().on('change', function() {
    $('.editor-preview').html(aceEditor.getValue().replace(/\r?\n|\r/g, ""));
  });

  $('.editor-preview').keyup(function() {
    //var value = $(this).val();
    var contentAttr = $(this).attr('class');
    if (!codeMode) {
      var value = $(this).val();
      //$( '.' + contentAttr + '' ).html(value);
      $('.' + contentAttr).html(value);
    } else {
      //$( '.' + contentAttr + '' ).html(htmlDiv.text());
      $('.' + contentAttr).html(htmlDiv.text());
    }
  });
});
#editControls {
  overflow: auto;
  border-top: 1px solid transparent;
  border-left: 1px solid transparent;
  border-right: 1px solid transparent;
  border-color: silver;
  border-top-left-radius: 5px;
  border-top-right-radius: 5px;
  padding: .5em 1em .5em 1em;
  background-color: #fbfbfb;
  margin: 0 auto;
  width: 100%;
  /*90*/
}

#editor {
  resize: vertical;
  overflow: auto;
  border: 1px solid silver;
  border-bottom-right-radius: 5px;
  border-bottom-left-radius: 5px;
  min-height: 100px;
  padding: 1em;
  background: white;
  margin: 0 auto;
  width: 100%;
  /*90*/
}

#editor:focus {
  outline: none !important;
  box-shadow: inset 0 0 2px silver;
}

.codeMode {
  font-family: Courier New, Source Code Pro Light, Medium, Source Code Pro ExtraLight, Menlo, Consola, Monaco Linux, Consola Regular, Fira Code Regular, DejaVu Sans Mono;
  /*font-family: inherit;*/
  /*font-family: 'Courier New';*/
  border: 0px;
  font-style: inherit;
  font-variant: inherit;
  font-weight: inherit;
  font-stretch: inherit;
  line-height: inherit;
  vertical-align: baseline;
  box-sizing: inherit;
  color: #fff;
}

.black-bg-colr {
  background-color: #000 !important;
}

.btn-group {
  position: relative;
  display: inline-block;
  font-size: 0;
  vertical-align: middle;
  white-space: nowrap;
}

.btn-group+.btn-group {
  margin-left: 5px;
}

.btn-group a {
  text-decoration: none;
}

.btn-editor {
  height: 30px;
  display: inline-block;
  padding: 6px 12px;
  margin-bottom: 0;
  font-size: 11px;
  font-weight: normal;
  line-height: 1.42857143;
  text-align: center;
  white-space: nowrap;
  vertical-align: middle;
  -ms-touch-action: manipulation;
  touch-action: manipulation;
  cursor: pointer;
  -webkit-user-select: none;
  -moz-user-select: none;
  -ms-user-select: none;
  user-select: none;
  background-image: none;
  border-radius: 4px;
  border: 1px solid transparent;
  color: #333;
  background-color: #fff;
  border-color: #ccc;
}

.btn-group>.btn-editor:first-child {
  margin-left: 0;
  -webkit-border-top-left-radius: 4px;
  -moz-border-radius-topleft: 4px;
  border-top-left-radius: 4px;
  -webkit-border-bottom-left-radius: 4px;
  -moz-border-radius-bottomleft: 4px;
  border-bottom-left-radius: 4px;
}

.btn-group>.btn-editor+.btn-editor {
  margin-left: -1px !important;
  border-top-right-radius: 4px;
  border-bottom-right-radius: 4px;
}

.btn-not-space {
  position: relative;
  float: left;
  margin-left: 0 !important;
  border-radius: inherit;
  border: 1px solid transparent;
  border-color: #ccc;
}

.btn-editor.btn-not-space:hover {
  background-color: rgba(230, 230, 230, 0.32);
}

#preview {
  padding: 1em;
  margin: 0 auto;
  width: 97%;
  border-top: 1px dotted #c8ccd0;
  border-bottom: 1px dotted #c8ccd0;
  clear: both;
}

#ace-editor {
  resize: vertical;
  overflow: auto;
  border: 1px solid silver;
  border-bottom-right-radius: 5px;
  border-bottom-left-radius: 5px;
  min-height: 100px;
  padding: 1em;
  margin: 0 auto;
  width: 100%;
  display: none;
}
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>WYSIWYG</title>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/ace/1.4.7/ace.js"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/ace/1.4.7/ext-beautify.js"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/js-beautify/1.10.3/beautify-html.min.js"></script>
</head>

<body>
  <div class="editor-wrapper">
    <div id="editControls">
      <div class="btn-group">
        <a class="btn-editor btn-not-space" data-role="bold" data-ref="#"><b>Bold</b></a>
        <a class="btn-editor btn-not-space" data-role="italic" data-ref="#"><em>Italic</em></a>
        <a class="btn-editor btn-not-space" data-role="underline" data-ref="#"><u><b>U</b></u></a>
        <a class="btn-editor btn-not-space" data-role="strikeThrough" data-ref="#"><strike>abc</strike></a>
      </div>
      <div class="btn-group">
        <a class="btn-editor btn-not-space" data-role="code" data-ref="#"><code>&lt;/&gt;</code></a>
      </div>
    </div>
    <div id="editor" contenteditable></div>
    <div id="ace-editor"></div>
    <textarea id="textarea" name="detail" class="editor-preview" style="display: none;"></textarea>
    <div id="preview" class="editor-preview"></div>
  </div>

</body>

</html>

Ace Editor with <p> Only:

$(function () {
    var aceEditor = ace.edit("ace-editor");
    aceEditor.setTheme("ace/theme/eclipse");
    aceEditor.session.setMode("ace/mode/html");

    $('#editControls a').click(function (e) {
        switch ($(this).data('role')) {
            case 'p':
                document.execCommand('formatBlock', false, $(this).data('role'));
                break;
            //Specific control for the code button.
            case 'code':
                //enable / disable code mode
                codeMode = !codeMode;
                if (codeMode) {
                    // ON: show the code in text mode
                    var editor = $("#editor");
                    var $aceEditor = $("#ace-editor");

                    var formattedHtml = html_beautify(htmlDiv.html());

                    aceEditor.setValue(formattedHtml, -1);

                    editor.css("display", "none");
                    $aceEditor.css("display", "block");

                } else {
                    // OFF: reinterpret the code
                    htmlDiv.html(htmlDiv.text().replace(/\r?\n|\r/g, ""));
                    var editor = $("#editor");
                    var $aceEditor = $("#ace-editor");

                    editor.css("display", "block");
                    $aceEditor.css("display", "none");

                    htmlDiv.html(aceEditor.getValue().replace(/\r?\n|\r/g, ""));
                }
                break;
            default:
                document.execCommand($(this).data('role'), false, null);
                break;
        }
    });

    //code mode control
    let codeMode = false;
    let htmlDiv = $("#editor");

    htmlDiv.on("paste", function (e) {
        e.preventDefault();

        var text = (e.originalEvent || e).clipboardData.getData('text/plain');

        document.execCommand('formatBlock', false, 'p');
        document.execCommand('insertText', false, text);
    });

    //input -> NOT keyup
    htmlDiv.on("input", function (e) {
        document.execCommand('formatBlock', false, 'p');
        //htmlDiv.on("keyup", function(e) {
        $(".editor-preview").val(htmlDiv.html());
        //$("#textarea").html(htmlDiv.html());
        $(".editor-preview").keyup();
        //$(".editor-preview").html(htmlDiv.html());
    });
    //$("#textarea").html(htmlDiv.html());

    aceEditor.getSession().on('change', function () {
        $('.editor-preview').html(aceEditor.getValue().replace(/\r?\n|\r/g, ""));
    });

    $('.editor-preview').keyup(function () {
        //var value = $(this).val();
        var contentAttr = $(this).attr('class');
        if (!codeMode) {
            var value = $(this).val();
            //$( '.' + contentAttr + '' ).html(value);
            $('.' + contentAttr).html(value);
        } else {
            //$( '.' + contentAttr + '' ).html(htmlDiv.text());
            $('.' + contentAttr).html(htmlDiv.text());
        }
    });
});
#editControls {
  overflow: auto;
  border-top: 1px solid transparent;
  border-left: 1px solid transparent;
  border-right: 1px solid transparent;
  border-color: silver;
  border-top-left-radius: 5px;
  border-top-right-radius: 5px;
  padding: .5em 1em .5em 1em;
  background-color: #fbfbfb;
  margin: 0 auto;
  width: 100%;
  /*90*/
}

#editor {
  resize: vertical;
  overflow: auto;
  border: 1px solid silver;
  border-bottom-right-radius: 5px;
  border-bottom-left-radius: 5px;
  min-height: 100px;
  padding: 1em;
  background: white;
  margin: 0 auto;
  width: 100%;
  /*90*/
}

#editor:focus {
  outline: none !important;
  box-shadow: inset 0 0 2px silver;
}

.codeMode {
  font-family: Courier New, Source Code Pro Light, Medium, Source Code Pro ExtraLight, Menlo, Consola, Monaco Linux, Consola Regular, Fira Code Regular, DejaVu Sans Mono;
  /*font-family: inherit;*/
  /*font-family: 'Courier New';*/
  border: 0px;
  font-style: inherit;
  font-variant: inherit;
  font-weight: inherit;
  font-stretch: inherit;
  line-height: inherit;
  vertical-align: baseline;
  box-sizing: inherit;
  color: #fff;
}

.black-bg-colr {
  background-color: #000 !important;
}

.btn-group {
  position: relative;
  display: inline-block;
  font-size: 0;
  vertical-align: middle;
  white-space: nowrap;
}

.btn-group+.btn-group {
  margin-left: 5px;
}

.btn-group a {
  text-decoration: none;
}

.btn-editor {
  height: 30px;
  display: inline-block;
  padding: 6px 12px;
  margin-bottom: 0;
  font-size: 11px;
  font-weight: normal;
  line-height: 1.42857143;
  text-align: center;
  white-space: nowrap;
  vertical-align: middle;
  -ms-touch-action: manipulation;
  touch-action: manipulation;
  cursor: pointer;
  -webkit-user-select: none;
  -moz-user-select: none;
  -ms-user-select: none;
  user-select: none;
  background-image: none;
  border-radius: 4px;
  border: 1px solid transparent;
  color: #333;
  background-color: #fff;
  border-color: #ccc;
}

.btn-group>.btn-editor:first-child {
  margin-left: 0;
  -webkit-border-top-left-radius: 4px;
  -moz-border-radius-topleft: 4px;
  border-top-left-radius: 4px;
  -webkit-border-bottom-left-radius: 4px;
  -moz-border-radius-bottomleft: 4px;
  border-bottom-left-radius: 4px;
}

.btn-group>.btn-editor+.btn-editor {
  margin-left: -1px !important;
  border-top-right-radius: 4px;
  border-bottom-right-radius: 4px;
}

.btn-not-space {
  position: relative;
  float: left;
  margin-left: 0 !important;
  border-radius: inherit;
  border: 1px solid transparent;
  border-color: #ccc;
}

.btn-editor.btn-not-space:hover {
  background-color: rgba(230, 230, 230, 0.32);
}

#preview {
  padding: 1em;
  margin: 0 auto;
  width: 97%;
  border-top: 1px dotted #c8ccd0;
  border-bottom: 1px dotted #c8ccd0;
  clear: both;
}

#ace-editor {
  resize: vertical;
  overflow: auto;
  border: 1px solid silver;
  border-bottom-right-radius: 5px;
  border-bottom-left-radius: 5px;
  min-height: 100px;
  padding: 1em;
  margin: 0 auto;
  width: 100%;
  display: none;
}
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>WYSIWYG</title>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/ace/1.4.7/ace.js"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/ace/1.4.7/ext-beautify.js"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/js-beautify/1.10.3/beautify-html.min.js"></script>
</head>

<body>
  <div class="editor-wrapper">
    <div id="editControls">
      <div class="btn-group">
        <a class="btn-editor btn-not-space" data-role="bold" data-ref="#"><b>Bold</b></a>
        <a class="btn-editor btn-not-space" data-role="italic" data-ref="#"><em>Italic</em></a>
        <a class="btn-editor btn-not-space" data-role="underline" data-ref="#"><u><b>U</b></u></a>
        <a class="btn-editor btn-not-space" data-role="strikeThrough" data-ref="#"><strike>abc</strike></a>
      </div>
      <div class="btn-group">
        <a class="btn-editor btn-not-space" data-role="code" data-ref="#"><code>&lt;/&gt;</code></a>
      </div>
    </div>
    <div id="editor" contenteditable></div>
    <div id="ace-editor"></div>
    <textarea id="textarea" name="detail" class="editor-preview" style="display: none;"></textarea>
    <div id="preview" class="editor-preview"></div>
  </div>

</body>

</html>