Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Prevent deletion of all <li> tags in a content editable <ul>

When you backspace all the way to the beginning of a <li> tag within a content editable <ul> tag, all of the remaining <li> tags are deleted.

<ul contenteditable="true">
    <li>Hello</li>
    <li>World</li>
</ul>

jsfiddle: http://jsfiddle.net/8Q53V/

Put your cursor after the 'd' in world and backspace all the way to the beginning of the line. When you hit backspace again, it will also delete the 'Hello' list item. How can you prevent this behavior?

UPDATE: It looks like this behavior only exists in Chrome

like image 849
tilleryj Avatar asked Jun 12 '14 15:06

tilleryj


2 Answers

This is what I have found:

The erronous behavior of contenteditable lists in Opera and Chrome only occurs if the parent ul element is not followed by any other element with a minimum rendering space of 1px x 1px. The display value of the element doesn't matter, as long as it is non-empty or has a set height dimension with CSS.

So adding a simple <div style="height: 1px;"> will answer your most direct question.

However, You can't reliably use contenteditable lists in HTML at the time of asking.

This question intrigued me and I tested it on Moz FF 29-30, Opera 21, Google Chrome 35 & IE 11, on Win 8.1 . I tested simple keys like Enter, Backspace and Delete.

TL;DR: IE 11 completely messes up, Opera & Chrome do pretty good if the ul is followed by an element with a rendered size, and Moz FF inserts a weird <br type="_moz"></br> tag. You would need e.preventDefault(), e.keyCode/charCode and the Selection & Range JS API to create a custom contenteditable list that works consistently across all browsers. However, these prerequisites already don't work consistently on all browsers. Best solution: use existing editors or some invisible text input for input.

These are the detailed gotcha's:


Hitting Enter to add a new li will add the following markup:

  • Opera and Google Chrome: <li><br><li>
  • Moz FF: <li><br type="_moz"></br>
  • IE 11: if the active li is non-empty: <li><br></li>, if it is empty, IE completely messes up & inserts a p, then duplicates an empty <ul contenteditable=""> after it, with only a <br> tag inside. This empty ul will be added after the element following the original element, if any.

Hitting Backspace to remove a li:not(:first-child) will do the following:

  • Opera and Google Chrome: if the ul is not followed by an element with a rendered size, all li's will be deleted. If it is, the expected behavior occurs. (one li is removed).
  • Moz FF: The expected behavior occurs (one li is removed). However, Moz FF moves the cursor out of the contenteditable area after removing a li.
  • IE 11: Same behavior as when hitting Enter.

Hitting Backspace to remove the first li should be prevented at all costs.

For every browser tested, this breaks the list lay-out. All new Enter hits will generate div's as direct children of the list.


Hitting Delete to remove a subsequent li works consistently across all tested browsers. The subsequent li will be removed, and any content inside it will be transferred to the current li


Typing the first letter in an empty li will do the following:

  • Opera, IE11 and Google Chrome: The letter is printed. The automatically inserted <br> tag is removed.
  • Moz FF: The letter is printed. The custom <br type="_moz"> tag is not removed.

Removing the only letter in an empty li will do the following:

  • Opera, IE11 and Google Chrome: Another <br> tag is automatically inserted.
  • Moz FF: Nothing. The custom <br type="_moz"> is still present.

Fiddle: http://jsfiddle.net/8Q53V/8/ (first ul is with custom coded behavior, not perfect, second ul is standard)

like image 147
webketje Avatar answered Oct 12 '22 08:10

webketje


You need to use contenteditable on each of the li tag

Demo

If you don't want to assign the attribute by yourself, you can use Javascript setAttribute() which will take care of that - (You've tagged Javascript in your question so I assume you are open to Javascript solution)

var elm = document.querySelectorAll('ul li');
for (var i=0; i < elm.length; i++) {
    elm[i].setAttribute("contenteditable", "true");
}

Demo (Assigning attribute using Javascript)

Just take a note over here, the selector am using in querySelectorAll() is too general, will match ALL the ul elements in your document, so if you want to make a specific group of li elements editable, than assign a class or an id to your ul element and appropriately define a unique selector.

If you want multiple ul li editable, it would be better to define a class instead of an id as id has to be unique... So it would be cumbersome to define unique everytime you want a group of li to be editable.

like image 29
Mr. Alien Avatar answered Oct 12 '22 10:10

Mr. Alien