Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to remove H1 formatting within ContentEditable (wysiwyg)

With the exception of using Undo, I don't think there's a way to remove h1 and h2 tags in content editable. The expected behavior is clicking the H1 button again should toggle it off, but it does not. There's also a "remove formatting" button, but it only works on items that are bold, italic, etc. Is there a way to do this through javascript?

Edit: Result must remove the opening and closing H1 tag, and not replace it with anything else.

Please see the simplified test case here: http://jsfiddle.net/kthornbloom/GSnbb/1/

<div id="editor" contenteditable="true">
    <h1>This is a heading one</h1>
    How can I remove the header styling if I want to?
</div>
like image 655
kthornbloom Avatar asked Aug 20 '13 14:08

kthornbloom


1 Answers

I decided to implement the approach I outlined in my comment to my other answer: traversing nodes within the selected range and removing particular nodes (in this case, based on tag name).

Here's the full demo. It won't work in IE <= 8 (which lacks DOM Range and Selection support) but will in everything other major current browser. One problem is that the selection isn't always preserved, but that isn't too hard to achieve.

http://jsfiddle.net/gF3sa/1/

This example includes modified range traversal code from elsewhere on SO.

function nextNode(node) {
    if (node.hasChildNodes()) {
        return node.firstChild;
    } else {
        while (node && !node.nextSibling) {
            node = node.parentNode;
        }
        if (!node) {
            return null;
        }
        return node.nextSibling;
    }
}

function getRangeSelectedNodes(range, includePartiallySelectedContainers) {
    var node = range.startContainer;
    var endNode = range.endContainer;
    var rangeNodes = [];

    // Special case for a range that is contained within a single node
    if (node == endNode) {
        rangeNodes = [node];
    } else {
        // Iterate nodes until we hit the end container
        while (node && node != endNode) {
            rangeNodes.push( node = nextNode(node) );
        }

        // Add partially selected nodes at the start of the range
        node = range.startContainer;
        while (node && node != range.commonAncestorContainer) {
            rangeNodes.unshift(node);
            node = node.parentNode;
        }
    }

    // Add ancestors of the range container, if required
    if (includePartiallySelectedContainers) {
        node = range.commonAncestorContainer;
        while (node) {
            rangeNodes.push(node);
            node = node.parentNode;
        }
    }

    return rangeNodes;
}

function getSelectedNodes() {
    var nodes = [];
    if (window.getSelection) {
        var sel = window.getSelection();
        for (var i = 0, len = sel.rangeCount; i < len; ++i) {
            nodes.push.apply(nodes, getRangeSelectedNodes(sel.getRangeAt(i), true));
        }
    }
    return nodes;
}

function replaceWithOwnChildren(el) {
    var parent = el.parentNode;
    while (el.hasChildNodes()) {
        parent.insertBefore(el.firstChild, el);
    }
    parent.removeChild(el);
}

function removeSelectedElements(tagNames) {
    var tagNamesArray = tagNames.toLowerCase().split(",");
    getSelectedNodes().forEach(function(node) {
        if (node.nodeType == 1 &&
                tagNamesArray.indexOf(node.tagName.toLowerCase()) > -1) {
            // Remove the node and replace it with its children
            replaceWithOwnChildren(node);
        }
    });
}

removeSelectedElements("h1,h2,h3,h4,h5,h6");
like image 151
Tim Down Avatar answered Oct 21 '22 17:10

Tim Down