How to wrap with HTML tags a cross-boundary DOM selection range?

后端 未结 3 488
难免孤独
难免孤独 2020-12-24 15:02

Right now I\'m capturing users\' text selections through s = window.getSelection() and range = s.getRangeAt(0) (browser\'s impls aside). W

相关标签:
3条回答
  • 2020-12-24 15:11

    That is when you add contentEditable=true attribute to the parent of those paragraphs, select any text, even across paragraphs, then make the call

    document.execCommand('italic', false, null);
    

    and finally if desired set contentEditable attribute back to false.

    Btw, this works on IE too, except that to enter editable mode I think it is called designMode or something, google for it.

    0 讨论(0)
  • 2020-12-24 15:13

    have you tried the following approach (which is actually the description in the W3C spec of what surroundContents should do):

    var wrappingNode = document.createElement("div");
    wrappingNode.appendChild(range.extractContents());
    range.insertNode(wrappingNode);
    
    0 讨论(0)
  • 2020-12-24 15:20

    I'm currently working on an inline editor and I've written a function that can properly wrap a cross-element range with any type of element like the execCommand does.

    function surroundSelection(elementType) {
        function getAllDescendants (node, callback) {
    
            for (var i = 0; i < node.childNodes.length; i++) {
                var child = node.childNodes[i];
                getAllDescendants(child, callback);
                callback(child);
            }
    
        }
    
        function glueSplitElements (firstEl, secondEl){
    
            var done = false,
                result = [];
    
            if(firstEl === undefined || secondEl === undefined){
                return false;
            }
    
            if(firstEl.nodeName === secondEl.nodeName){
                result.push([firstEl, secondEl]);
    
                while(!done){
                    firstEl = firstEl.childNodes[firstEl.childNodes.length - 1];
                    secondEl = secondEl.childNodes[0];
    
                    if(firstEl === undefined || secondEl === undefined){
                        break;
                    }
    
                    if(firstEl.nodeName !== secondEl.nodeName){
                        done = true;
                    } else {
                        result.push([firstEl, secondEl]);
                    }
                }
            }
    
            for(var i = result.length - 1; i >= 0; i--){
                var elements = result[i];
                while(elements[1].childNodes.length > 0){
                    elements[0].appendChild(elements[1].childNodes[0]);
                }
                elements[1].parentNode.removeChild(elements[1]);
            }
    
        }
    
        // abort in case the given elemenType doesn't exist.
        try {
            document.createElement(elementType);
        } catch (e){
            return false;
        }
    
        var selection = getSelection();
    
        if(selection.rangeCount > 0){
            var range = selection.getRangeAt(0),
                rangeContents = range.extractContents(),
                nodesInRange  = rangeContents.childNodes,
                nodesToWrap   = [];
    
            for(var i = 0; i < nodesInRange.length; i++){
                if(nodesInRange[i].nodeName.toLowerCase() === "#text"){
                    nodesToWrap.push(nodesInRange[i]);
                } else {
                    getAllDescendants(nodesInRange[i], function(child){
                        if(child.nodeName.toLowerCase() === "#text"){
                            nodesToWrap.push(child);
                        }
                    });
                }
            };
    
    
            for(var i = 0; i < nodesToWrap.length; i++){
                var child = nodesToWrap[i],
                    wrap = document.createElement(elementType);
    
                if(child.nodeValue.replace(/(\s|\n|\t)/g, "").length !== 0){
                    child.parentNode.insertBefore(wrap, child);
                    wrap.appendChild(child);
                } else {
                    wrap = null;
                }
            }
    
            var firstChild = rangeContents.childNodes[0];
            var lastChild = rangeContents.childNodes[rangeContents.childNodes.length - 1];
    
            range.insertNode(rangeContents);
    
            glueSplitElements(firstChild.previousSibling, firstChild);
            glueSplitElements(lastChild, lastChild.nextSibling);
    
            rangeContents = null;
        }
    };
    

    Here's a JSFiddle with some complex HTML as demo: http://jsfiddle.net/mjf9K/1/. Please note that I took this straight out of my application. I use a few helpers to correctly restore the range to the original selection etc. These are not included.

    0 讨论(0)
提交回复
热议问题