Closing <ul /> tags in contenteditable without inserting superfluous tags

contenteditable, javascript, jquery

Solution

Similar to your attempt, we modify the contenteditable when the user presses Enter: Demo

if (window.getSelection) { // w3c
    $('div').keypress(function (e) {
        var sel, node, children, br, range;
        if (e.which == 13) {
            sel = window.getSelection();
            node = $(sel.anchorNode);
            children = $(sel.anchorNode.childNodes);

            // if nothing is selected and the caret is in an empty <li>
            // (the browser seems to insert a <br> before we get called)
            if (sel.isCollapsed && node.is('li') && (!children.length ||
                    (children.length == 1 && children.first().is('br')))) {
                e.preventDefault();

                // if the empty <li> is in the middle of the list,
                // move the following <li>'s to a new list
                if (!node.is(':last-child')) {
                    node.parent().clone(false)
                        .empty()
                        .insertAfter(node.parent())
                        .append(node.nextAll());
                }

                // insert <br> after list
                br = $('<br>').insertAfter(node.parent());

                // move caret to after <br>
                range = document.createRange();
                range.setStartAfter(br.get(0));
                range.setEndAfter(br.get(0));
                sel.removeAllRanges();
                sel.addRange(range);

                // remove <li>
                node.remove();
            }
        }
    });

} else if (document.selection) { // internet explorer
    $('div').keypress(function (e) {
        var range, node, children;
        if (e.which == 13) {
            range = document.selection.createRange();
            node = $(range.parentElement());
            children = $(range.parentElement().childNodes);

            // if nothing is selected and the caret is in an empty <li>
            // (the browser seems to insert a <br> before we get called)
            if (!range.htmlText.length && node.is('li') && (!children.length ||
                    (children.length == 1 && children.first().is('br')))) {
                e.preventDefault();

                // if the empty <li> is in the middle of the list,
                // move the following <li>'s to a new list
                if (!node.is(':last-child')) {
                    node.parent().clone(false)
                        .empty()
                        .insertAfter(node.parent())
                        .append(node.nextAll());
                }

                // insert <br> after list
                br = $('<br>').insertAfter(node.parent());

                // move caret to after <br>
                range = document.body.createTextRange();
                range.moveToElementText(br.get(0));
                range.collapse(false);
                range.select();

                // remove <li>
                node.remove();
            }
        }
    });
}

Note that this doesn't handle the case where the user has selected something before pressing Enter. If you want to handle this case, you'll need to figure out if the user has selected the entire contents of the `<li>` (this doesn't seem like a trivial task), and if so, delete the contents and treat it the same as if the user pressed Enter in an empty `<li>`.

Problem

Working with an unordered (or ordered) list in a contenteditable gives me a headache. Whenever I want to end editing the list by pressing ENTER twice the browser will close the `<ul />` but inserts a `<p />` (Firefox) or a `<div />` (Chrome) tag that contains a `<br />`. Example here My goal is to avoid that superfluous `<p />` or `<div />` and instead just close the `<ul />`. I have tried to modify Tim Down's solution which will prevent the browser to insert `<p />` or `<div />` when pressing ENTER and instead inserts a clean `<br />` tag. Example here Unfortunately though, when using that solution the `<ul />` is never closed by the browser since only `<br />` tags are inserted inside the `<li />` item. So my question is: How can I actively close the `<ul />` by inserting a node or pasting html when pressing enter on the last empty `<li />`? Update: In case the question is stll unclear: I am looking for a way to close the `<ul />` without inserting `<p />` or `<div />` tags but just by inserting a good old plain `<br />` instead

Original source

Related problems