Search code examples
javascripthtmlgoogle-chromeexeccommand

Styles getting lost using document.execCommand("insertorderedlist") in Chrome


The following snippet loses custom styles on Chrome, but not on Firefox:

    document.getElementById( 'run' ).addEventListener( 'click', function() {
      document.execCommand("insertorderedlist");
    } );
  <div contenteditable="true">
    <p><span style="font-weight: bold;">line 1</span></p>
    <p><span style="font-style: italic;">line 2</span></p>
    <p><span style="text-decoration-line: underline;">line 3</span></p>
  </div>
  
  <button id="run">Select the above text and click me</button>

Is there a known workaround for this? I can't find any documentation about it.


Solution

  • You would need to get the style of the selections common ancestor (which, in this case, is a text node, so you'll have to get the parentNode), and apply it to the new selections common ancestor:

    document.getElementById( 'run' ).addEventListener( 'click', function() {
            sel = window.getSelection();
            if (sel.rangeCount) {
                let selection = sel.getRangeAt(0);
                if (selection.startContainer.parentNode.tagName === 'SPAN') {
                  let startNode = selection.startContainer.parentNode.parentNode;
                  let endNode = selection.endContainer.parentNode.parentNode;
                  let styles = [startNode.firstChild.style.cssText];
                  while(startNode && !startNode.isEqualNode(endNode)) {
                      startNode = startNode.nextElementSibling || startNode.nextSibling;
                      styles.push(startNode.firstChild.style.cssText);
                  }
                  document.execCommand("insertorderedlist");
                  selection = sel.getRangeAt(0);
                  startNode = selection.startContainer.parentNode;
                  endNode = selection.endContainer.parentNode;
                  newNodes = [startNode];
                  while(startNode && !startNode.isEqualNode(endNode)) {
                      startNode = startNode.nextElementSibling || startNode.nextSibling;
                      newNodes.push(startNode);
                  }
                  for (var i = 0; i < newNodes.length; i++) {
                     newNodes[i].style = styles[i];
                  }
                } else {
                  if (sel.rangeCount) {
                      let ancestor = sel.getRangeAt(0).commonAncestorContainer.parentNode;
                      document.execCommand("insertorderedlist");
                      sel.getRangeAt(0).commonAncestorContainer.parentNode.style = ancestor.style.cssText;
                  }
              }
            }
            
        } );
    <div contenteditable="true">
        <p><span style="font-weight: bold;">line 1</span></p>
        <p><span style="font-style: italic;">line 2</span></p>
        <p><span style="text-decoration-line: underline;">line 3</span></p>
      </div>
      
      <button id="run">Select the above text and click me</button>