Reputation: 315
I want to allow the editing of an ordered list using contenteditable. When the user changes or adds in a new element in the list I want to be able to manipulate the text (e.g. wrap in span tag, replace text etc).
I've created a listener for the Enter key and can get the last list element value. I've tried to change this and replace with the new value. However this populates the new list element created on the enter press.
<div>
<ol contenteditable=true class="editor">
<li><br></li>
</ol>
</div>
$('.editor' ).on('keydown .editable', function(e){
if ( e.keyCode === 13 ) {
var insertText = "<span>"+e.target.lastElementChild.innerText+"</span>";
e.target.lastElementChild.innerHTML = insertText;
return true
}
});
What is the best way to implement this functionality for new entries anywhere in the list not just the end? Open to Jquery solutions
example jsfiddle
Upvotes: 4
Views: 1861
Reputation: 31
hello you might wanna check this let me give you a heads up. instead of using the
lastchild.innerHTML
replace it with
nextSibling.innerHTML
like this
$('.editor' ).on('keydown .editable', function(e){
if ( e.keyCode === 13 ) {
var insertText = "<span>"+e.target.lastElementChild.innerText+"</span>";
e.target.nextSibling.innerHTML = insertText;
return true
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div>
<ol contenteditable=true class="editor">
<li><br></li>
</ol>
</div>
$('.editor' ).on('keydown .editable', function(e){
if ( e.keyCode === 13 ) {
var insertText = "<span>"+e.target.lastElementChild.innerText+"</span>";
e.target.nextSibling.innerHTML = insertText;
return true
}
});
Upvotes: 0
Reputation: 13983
You could use a MutationObserver to detect when a child is added to your list, and then update the previousSibling
to wrap it in a <span>
:
function subscriber(mutations) {
mutations.forEach(mutation => {
mutation.addedNodes.forEach(node => {
const prev = node.previousSibling;
if (prev) {
prev.innerHTML = `<span>${prev.innerHTML.replace(/<br>$/, '')}</span>`;
}
});
});
}
const observer = new MutationObserver(subscriber);
observer.observe(document.querySelector('ol[contenteditable]'), { childList: true });
.editor span::after {
content: '😀';
}
<ol contenteditable class="editor">
<li>First li</li>
</ol>
Upvotes: 2
Reputation: 1928
Instead of taking action when an edit happens, you could set an interval that will modify the html of the li
elements as desired.
setInterval(function(){
$('.editor' ).find('li').each(function(){
if ($(this).html().indexOf('span')==-1){
$(this).html('<span>' + $(this).html() + '</span>');
}
});
}, 200);
Upvotes: 0
Reputation: 24965
You could bind your logic to the last li
, and perform your logic from the events it emits.
$('.editor .last-item')
.on('click', function(e){
// clear out the html so the user can just type
e.target.innerHTML = '';
})
.on('keydown', function(e){
if (e.keyCode === 13) {
// ignore the enter key so it doesn't insert a new like
e.preventDefault();
// create the new li before the last one
$('<li>'+ e.target.innerHTML.trim() +'</li>').insertBefore(e.target);
// clear out the html so the user can keep entering items
e.target.innerHTML = '';
}
})
.on('blur', function(e){
// if the user leaves the field, change it back to the instructional text
e.target.innerHTML = 'Click to enter New List Item';
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div>
<ol class="editor">
<li class="last-item" contenteditable=true>Click to enter New List Item</li>
</ol>
</div>
Upvotes: 0