Reputation: 1165
I have a div which is being used as a user-friendly text editor on my school-project website. However if I select all of the text inside the editor and attemp to surround it with <code> tags </code>
I can not get the cursor out of it, it keeps adding whatever i type into the same tag, which is <code>
.
After some time I gave up on it, but then I found this answer and tried to modify the jsfiddle that is mentioned there in the hopes of getting it to work. But no luck.
If I, lets say, select the last word in any line and surround it with <code>
tags, I am able to get the cursor out of it where there is non-surrounded text but on the end there's no plaintext to jump onto so the cursor is stuck in <code>
How can I make sure that the cursor doesn't get stuck in added tags?
maybe a way to move the cursor out of it?
jsFiddle
function surroundSelection() {
var code = document.createElement("code");
code.style.fontStyle = "italic";
code.style.color = "#333";
code.style.background = "#ddd";
if (window.getSelection) {
var sel = window.getSelection();
if (sel.rangeCount) {
var range = sel.getRangeAt(0).cloneRange();
range.surroundContents(code);
sel.removeAllRanges();
sel.addRange(range);
}
}
}
div, input {
padding:10px;
}
div {border:1px solid;}
<input type="button" onclick="surroundSelection()" value="Surround">
<div contenteditable="true">One two three four</div>
Upvotes: 0
Views: 476
Reputation: 4768
A slightly quirky workaround here. No longer using surround but instead copying contents into the new node. We then add a space at the end. It it now possible to click after the code block, even if all text was selected.
EDIT: Added sel.collapseToEnd(); as per comment (thanks). It improves the experience by putting the user straight into normal typing context. However, the space is still required, it does not work without it.
EDIT: Now adds space to the start and end.
EDIT: Remove spacers for send to server.
function surroundSelection() {
let code = document.createElement("code");
code.style.fontStyle = "italic";
code.style.color = "#333";
code.style.background = "#ddd";
const newSpan = () => {
let span = document.createElement('span');
span.classList.add('codespacer');
span.innerHTML = ' ';
return span;
}
if (window.getSelection) {
let sel = window.getSelection();
if (sel.rangeCount) {
let range = sel.getRangeAt(0).cloneRange();
code.innerHTML = range.toString();
range.deleteContents();
range.insertNode(newSpan());
range.insertNode(code);
range.insertNode(newSpan());
sel.removeAllRanges();
sel.addRange(range);
sel.collapseToEnd();
}
}
}
function getContentForSave() {
let codeSection = document.querySelector('#codeSection');
// Copy into a temporary element (otherwise the text will change in the UI)
let tempSection = document.createElement('div');
tempSection.innerHTML = codeSection.innerHTML;
console.log(`Before: ${tempSection.innerHTML}`);
// Remove all codespacers
let spacers = tempSection.querySelectorAll('.codespacer');
for(let space of spacers) {
tempSection.removeChild(space);
}
console.log(`After: ${tempSection.innerHTML}`);
return tempSection.innerHTML;
}
div,
input {
padding: 10px;
}
div {
border: 1px solid;
}
<input type="button" onclick="surroundSelection()" value="Surround">
<div contenteditable="true" id="codeSection">One two three four</div>
<input type="button" onclick="getContentForSave();" value="Test For Save">
Upvotes: 1
Reputation: 11577
I think a way you can get over it is to append a empty space textNode
right after the code and then move the cursor to its position. This way user can immediately continue to write normal text.
Here's the break down:
document.createTextNode('\u00A0')
. Note, a literal ' '
didn't work on my browser (Chrome 70).div
.range
with range.setEnd()
so we can use selection.collapseToEnd()
method to move the cursor to the very end.Altogether (fiddle)
+ function addPad() {
+ var $input = document.querySelector('div');
+ var pad = document.createTextNode('\u00A0');
+ $input.appendChild(pad);
+ return pad;
+ }
function surroundSelection() {
var sel = window.getSelection();
if (!sel || !sel.rangeCount) return;
var code = document.createElement("code");
var range = sel.getRangeAt(0).cloneRange();
range.surroundContents(code);
sel.removeAllRanges();
sel.addRange(range);
+ // create empty node + add it to div
+ var padNode = addPad();
+ // update the current range to include the empty node.
+ // since we only add 1 space, the `offset` is set to 1.
+ range.setEnd(padNode, 1);
+ // move the cursor to very end of range
+ sel.collapseToEnd();
}
Upvotes: 1