Toggle any tag just like execCommand bold does with b

I have a contenteditable div and need to be able to wrap / unfold text in other tags, for example <code>

, etc. Wrapping them is not a problem, rather expanding the selected text, for example when there is <b>hello </b><code>world</code>

and I only need to expand the letters in the middle (orl), there seems to be no easy way to detect if the selected text is inside <code>

or not. So while document.execCommand("bold")

doing this wrapping / expanding operation with tags <b>

and <i>

, is there a way to do the same with tags other than b, i, u?

+3


source to share


2 answers


So after a lot of searching for a solution and looking for nothing, I decided to try and write it myself, so if anyone finds this piece useful, I would be glad. I don't know how it works in IE or Safari, and for sure it's not the most efficient way to do it, but for most browsers it should do the job.



<!doctype html>
<html>
    <head>
        <title>Wrap</title>
        <meta charset="utf-8">
    </head>
    <body>
        <script>
            function traverseParentCE(el){
                var parent = el.parentElement;
                if(!parent || (el && el.contentEditable=="true")) return el;
                while(parent && parent.contentEditable!="true"){
                    parent = parent.parentElement;
                }
                return parent;
            }
            function finishWrap(el, innHtml, marker, wrapper){
                el.innerHTML = innHtml.replace("<"+marker+">","").replace("</"+marker+">","");
                var node = el.getElementsByTagName(wrapper)[0];
                var range = document.createRange();
                range.setStart(node, 0);
                range.setEnd(node, 1);
                var sel = document.getSelection();
                sel.removeAllRanges();
                sel.addRange(range);
            }
            function wrap(tag){
                var marker = "marker";
                var wrapper = "inncnt";
                var ae = document.activeElement;
                if(ae.contentEditable!="true") return;
                var sel = document.getSelection();
                var range = sel.getRangeAt(0);
                var el = document.createElement(marker);
                el.appendChild(range.cloneContents());
                range.deleteContents();
                range.insertNode(el);
                var innHtml = el.innerHTML;
                var count = 0;
                while(true){    // I know right, this kind of replacing is horrible, but RegExp somehow didn't work for me
                    var pos = innHtml.indexOf("<"+tag+">");
                    if(pos==-1) break;
                    innHtml = innHtml.replace("<"+tag+">","").replace("</"+tag+">","");
                    count++;
                }
                innHtml.replace("<"+wrapper+">","").replace("</"+wrapper+">","");
                el.innerHTML = "<"+wrapper+">" + innHtml + "</"+wrapper+">";
                var container = traverseParentCE(range.commonAncestorContainer);
                innHtml = container.innerHTML;
                if(count>0){
                    return finishWrap(container, innHtml, marker, wrapper);
                }
                var pos = innHtml.indexOf("<"+marker+">");
                var contentBefore = innHtml.substr(0, pos);
                var lastStarting = contentBefore.indexOf("<"+tag+">");
                var lastEnding = contentBefore.indexOf("</"+tag+">");
                var wrap = false;
                if(lastStarting == -1) wrap = true;
                else if(lastStarting < lastEnding) wrap = true;
                if(wrap) innHtml = innHtml.replace(marker, tag).replace(marker, tag);
                else innHtml = innHtml.replace(marker, "/"+tag).replace("/"+marker,tag);
                var doublet = "<"+tag+"></"+tag+">";
                while(true){
                    var pos = innHtml.indexOf(doublet);
                    if(pos==-1) break;
                    innHtml = innHtml.replace(doublet, "");
                }
                return finishWrap(container, innHtml, marker, wrapper);
            }
        </script>
        <button onmousedown="wrap('b'); return false;" onmouseup="return false;" onclick="return false;">B</button>
        <button onmousedown="wrap('i'); return false;" onmouseup="return false;" onclick="return false;">I</button>
        <button onmousedown="wrap('code'); return false;" onmouseup="return false;" onclick="return false;">Code</button>
        <div style="width: 800px; height: 300px; background-color: yellow" contenteditable="true" id="out">
            Hello <b>World</b>! This <code>is some</code> sentence.
        </div>
    </body>
</html>

      

0


source


Modifying contenteditable divs is not an easy task. What you are asking for is possible, but too big a topic and there are not a few examples here.



However, there are many ready-made libraries that have done this before (for example CKEditor . They are very complex but also very flexible. Out of the box they probably do a lot more than you need, but can be customized so that functions you don't need , can be disabled, and they have an API that allows you to control them externally if needed.

0


source







All Articles