7

This function uses the range object to return user selection and wrap it in bold tags. is there a method that allows me to remove the tags? As in <b>text<b> = text.


I actually need a toggle function that wraps the selection in tags & un-wraps it if already contains tags. Similar to what text editors do when you toggle the bold button.

if "text" then "<b>text</b>"
else "<b>text</b>" then "text"  

...

function makeBold() {

    //create variable from selection
    var selection = window.getSelection();
        if (selection.rangeCount) {
        var range = selection.getRangeAt(0).cloneRange();
        var newNode = document.createElement("b");

            //wrap selection in tags
        range.surroundContents(newNode);

            //return the user selection
        selection.removeAllRanges();
        selection.addRange(range);
    } 
}
Andy E
  • 338,112
  • 86
  • 474
  • 445
Zebra
  • 3,858
  • 9
  • 39
  • 52

2 Answers2

5

I didn't mention this in your previous question about this because it sounded like you wanted a generic means of surrounding a range within an element, but for this particular application (i.e. bolding/unbolding text), and assuming you don't mind a little cross-browser variation in the precise tags used (<strong> versus <bold> versus possibly <span style="font-weight: bold">), you're best off using document.execCommand(), which will toggle boldness:

function toggleBold() {
    document.execCommand("bold", false, null);
}

This will work in all browsers when the selected content is editable, and even when it's not editable in IE. If you need it to work on non-editable content in other browsers, you'll need to temporarily make the document editable:

function toggleBold() {
    var range, sel;
    if (window.getSelection) {
        // Non-IE case
        sel = window.getSelection();
        if (sel.getRangeAt) {
            range = sel.getRangeAt(0);
        }
        document.designMode = "on";
        if (range) {
            sel.removeAllRanges();
            sel.addRange(range);
        }
        document.execCommand("bold", false, null);
        document.designMode = "off";
    } else if (document.selection && document.selection.createRange &&
            document.selection.type != "None") {
        // IE case
        range = document.selection.createRange();
        range.execCommand("bold", false, null);
    }
}
Tim Down
  • 318,141
  • 75
  • 454
  • 536
0

wrapping & un-wrapping the text selection using the same button click event handler:

boldBtn.addEventListener('click', () => {
  let selection = document.getSelection();
  
  const isAllowedContainer = selection.baseNode.parentElement?.closest?.('#editor');
  
  // do not continue if no text selection or this is not the desired element container
  if( selection.rangeCount < 1 || !isAllowedContainer ) return;
  
  const range = selection.getRangeAt(0);
  const selParent = selection.anchorNode?.parentElement;
  const selectedElem = selParent?.nodeType == 1 && selParent?.children.length < 2 && selParent;
  
  // un-wrap
  if(selectedElem.tagName === 'B') {
    selectedElem.replaceWith(...selectedElem.childNodes)
  }
 
  // wrap with <b>
  else {
    range.surroundContents(document.createElement("b"));
    selection.removeAllRanges();
    selection.addRange(range);
    range.collapse(); // removes selected and places caret at the end of the injected node
  }
})
<button id="boldBtn">B</button><br/><br/>
<div id='editor' contenteditable="true">Select some text and click the button</div>
vsync
  • 118,978
  • 58
  • 307
  • 400