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
The contenteditable attribute specifies whether the content of an element is editable or not. Note: When the contenteditable attribute is not set on an element, the element will inherit it from its parent.
The contenteditable global attribute is an enumerated attribute indicating if the element should be editable by the user. If so, the browser modifies its widget to allow editing.
You can set the HTML5 contenteditable attribute with the value true (i.e. contentEditable="true" ) to make an element editable in HTML, such as <div> or <p> element.
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>
.
I understand your question but it's not clear why such a requirement would exist. It might help to clarify that aspect.
Regardless, here's one idea. Why not replace or remove the empty <div>
and <p>
tags.
$(document).ready(function(){
$("div").keyup(function(evt) {
$("div, p").filter( function() {
$this = $(this);
return !($.trim($(this).text()).length);
}).replaceWith('<br />');
});
});
Working Example: http://jsfiddle.net/4xyR2/9/
One issue I notice concerns how contenteditable
affects the cursor using the above solution. I think Chrome and Firefox require the <div>
and <p>
so they can track where the cursor exists in the <div>
. This comes from my observations while testing, not from a deep understanding on how the browser's interpret contenteditable
.
Chrome (24.0.1312.52 m) appears to dislike the replace. I see weird cursor placement when I test it, but it works. Firefox (17.0.1) handles the replace nicely.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With