I have the markup with non-editbale spans with user-select: none inside contenteditable div. Simplified version of it looks like this:
<div contenteditable="true">
some random text
<span contenteditable="false" style="user-select: none">1</span><span contenteditable="false" style="user-select: none">2</span>
</div>
The key point here is that both spans are on one line. When I put a cursor at the end of line and press Backspace all content is deleted. When putting spans on different lines the code works as expected - Backspace
deletes only one last non-editable elements.
Problem occurs in Chrome. FF behaves strange as well, but in another way. I only need to make it work in Chrome for now.
Physically putting spans on different lines in markup solves the problem, but it's no use, because in my case I add non-editable elements to contenteditable dynamically.
const input = document.getElementById("snafu");
const button = document.getElementById("button");
const onClick = () => {
const tag = document.createElement("span");
tag.setAttribute("contenteditable", false);
tag.classList.add("xml-tag");
tag.innerHTML = "t";
input.append(tag);
};
button.addEventListener("click", onClick);
body {
font-family: sans-serif;
}
.input {
color: green;
box-shadow: 2px 2px 10px mediumaquamarine;
padding: 8px;
border-radius: 8px;
margin-bottom: 24px;
}
.xml-tag {
display: inline-flex;
justify-content: center;
align-items: center;
width: 16px;
height: 16px;
margin-inline: 2px;
user-select: none;
border-radius: 50%;
background: lime;
font-size: 10px;
white-space: pre;
}
.button {
background: transparent;
box-shadow: 3px 3px 5px limegreen;
border: none;
border-radius: 8px;
padding: 8px;
}
.button:hover {
transform: scale(1.1);
cursor: pointer;
}
<!DOCTYPE html>
<html>
<head>
<title>Parcel Sandbox</title>
<meta charset="UTF-8" />
</head>
<body>
<div id="app">
<div spellcheck="false" contenteditable="true" class="input" id="snafu">
<span>Add at least 2 non-editable tags by clicking the button below. Then
put the cursor at the end of line and press 'Backspace'</span
>
</div>
<button id="button" class="button">Add me a tag, daddy!</button>
</div>
<script src="src/index.js"></script>
</body>
</html>
UPD: When I tried to edit the post code snippet button appeared and I was able to add a snippet eventually.
Listen to keydown events of your textarea. If backspace (or delete) was pressed, get all tags and temporarily remove user-select: none. Then add a setTimeout(func, 0) to restore user-select: none after the last tag was deleted.
const input = document.getElementById("snafu");
const button = document.getElementById("button");
const onClick = () => {
const tag = document.createElement("span");
tag.setAttribute("contenteditable", false);
tag.classList.add("xml-tag");
tag.innerHTML = "t";
input.append(tag);
};
button.addEventListener("click", onClick);
// +++++ ADD THIS +++++
input.addEventListener("keydown", (event) => {
// if backspace or delete pressed
if (event.keyCode === 8 || event.keyCode === 46) {
// make tags selectable, so they can be removed
const tags = input.querySelectorAll(".xml-tag");
tags.forEach(tag => tag.style["user-select"] = "auto");
// make tags not selectable after deletion
setTimeout(function() {
tags.forEach(tag => tag.style["user-select"] = "none");
}, 0);
}
});
body {
font-family: sans-serif;
}
.input {
color: green;
box-shadow: 2px 2px 10px mediumaquamarine;
padding: 8px;
border-radius: 8px;
margin-bottom: 24px;
}
.xml-tag {
display: inline-flex;
justify-content: center;
align-items: center;
width: 16px;
height: 16px;
margin-inline: 2px;
user-select: none;
border-radius: 50%;
background: lime;
font-size: 10px;
white-space: pre;
}
.button {
background: transparent;
box-shadow: 3px 3px 5px limegreen;
border: none;
border-radius: 8px;
padding: 8px;
}
.button:hover {
transform: scale(1.1);
cursor: pointer;
}
<!DOCTYPE html>
<html>
<head>
<title>Parcel Sandbox</title>
<meta charset="UTF-8" />
</head>
<body>
<div id="app">
<div spellcheck="false" contenteditable="true" class="input" id="snafu">
<span
>Add at least 2 non-editable tags by clicking the button below. Then
put the cursor at the end of line and press 'Backspace'</span
>
</div>
<button id="button" class="button">Add me a tag, daddy!</button>
</div>
</body>
</html>