This question has already been asked but until now there is no working answer so I am tempting to open it again hopefully we can find a hack to it.
I have a contentEditable paragraph and a text input, when I select some text and click the input, the selection is gone.
So I've tried to save the selection on input mousedown and to restore it back on mouseup and yeah it works ( as expected in firefox) But... in chrome the input lose focus :(
See it in action (use chrome) : https://jsfiddle.net/mody5/noygdhdu/
this is the code I've used :
HTML
<p contenteditable="true">
Select something up here and click the input below
<br> on firefox the input get the focus and the text still selected.
<br> on chrome the text still selected but the input lose focus
</p>
<input type="text" id="special" style="border: solid blue 1px">
javascript
function saveSelection() {
if (window.getSelection) {
sel = window.getSelection();
if (sel.getRangeAt && sel.rangeCount) {
return sel.getRangeAt(0);
}
} else if (document.selection && document.selection.createRange) {
return document.selection.createRange();
}
return null;
}
function restoreSelection(range) {
if (range) {
if (window.getSelection) {
sel = window.getSelection();
sel.removeAllRanges();
sel.addRange(range);
} else if (document.selection && range.select) {
range.select();
}
}
}
var specialDiv = document.getElementById("special");
var savedSel = null;
specialDiv.onmousedown = function() {
savedSel = saveSelection(); // save the selection
};
specialDiv.onmouseup = function() {
restoreSelection(savedSel); // restore the selection
};
Input Text focus() Method The focus() method is used to give focus to a text field.
To detect if the element has the focus in JavaScript, you can use the read-only property activeElement of the document object. const elem = document. activeElement; The activeElement returns the currently focused element in the document.
As I can't comment on maioman (some reputation needed :)), here a little addition to his aswer:
The reason it doesn't work in firefox is that putting the focus on the input field removes the selection.
It all works fine if you put a mouseup event on the p instead of a focus event on the inputfield:
p.addEventListener('mouseup', () => { highlight(select()); // save the selection })
Replacing selection with a <span>
is probably the simplest way. You can also use an <iframe>
, which is what google uses in Google Docs to maintain selection of text inside document while clicking UI elements.
With <span>
, the solution might be like this (this solution build on your original code and the ideas of the other people here, especially @Bekim Bacaj).
!function(doc, win) {
var input = doc.getElementById('special')
, editable = doc.getElementById('editable')
, button = doc.getElementById('button')
, fragment = null
, range = null;
function saveSelection() {
if (win.getSelection) {
sel = win.getSelection();
if (sel.getRangeAt && sel.rangeCount) {
return sel.getRangeAt(0);
}
} else if (doc.selection && doc.selection.createRange) {
return doc.selection.createRange();
}
return null;
}
/* Not needed, unless you want also restore selection
function restoreSelection() {
if (range) {
if (win.getSelection) {
sel = win.getSelection();
sel.removeAllRanges();
sel.addRange(range);
} else if (doc.selection && range.select) {
range.select();
}
}
}
*/
function saveRangeEvent(event) {
range = saveSelection();
if (range && !range.collapsed) {
fragment = range.cloneContents();
toggleButton();
}
}
function toggleButton() {
button.disabled = !fragment || !input.value.match(/^https?:.*/);
}
toggleButton();
editable.addEventListener('mouseup', saveRangeEvent);
editable.addEventListener('keyup', saveRangeEvent);
button.addEventListener('click', function(event) {
// insert link
var link = doc.createElement('a');
link.href = input.value;
input.value = '';
range.surroundContents(link);
toggleButton();
});
input.addEventListener('keyup', toggleButton);
input.addEventListener('change', toggleButton);
input.addEventListener('mousedown', function(event) {
// create fake selection
if (fragment) {
var span = doc.createElement('span');
span.className = 'selected';
range.surroundContents(span);
}
});
input.addEventListener('blur', function(event) {
// remove fake selection
if (fragment) {
range.deleteContents();
range.insertNode(fragment);
//restoreSelection();
}
fragment = null;
}, true);
}(document, window)
.selected {
background-color: dodgerblue;
color: white;
}
<p id="editable" contenteditable="true">
Select something up here and click the input below
<br>on firefox the input get the focus and the text still selected.
<br>on chrome the text still selected but the input lose focus
</p>
<table>
<tr>
<td>
<input type="text" id="special" style="border: solid blue 1px" placeholder="insert valid link incl. http://">
</td>
<td>
<button id="button">Add link</button>
</td>
</tr>
</table>
Link to jsFiddle
I worked a little on this one... It was a really fun and instructive exercise.
I mainly started from Maioman's answer.
I made it so that selected text would end up in an anchor with the href provided in the input field... And the selected text remains selected while inputing the link. That is my understanding of your question.
See my working Fiddle : https://jsfiddle.net/Bes7weB/rLmfb043/
Tested to be working on FF 46, Chrome 50, Safari 5.1 and Explorer 11.
Notice that classList
is only supported in IE10 and later.
Also, the links are not "clickable" because of the mouseup event.
But you can see the title attribute on mouseover.
I assume you'll save the paragraph's innerHTML to output it somewhere else.
;)
CSS:
a.highlighted {
background: blue;
color:white;
}
HTML:
<h1>Select some text below and click GO!</h1>
<br>
<p contenteditable="true" tabindex="0">
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Mauris nec risus turpis. Donec nisi urna, semper nec ex ac, mollis egestas risus. Donec congue metus massa, nec lacinia tortor ornare ac. Nulla porttitor feugiat lectus ut iaculis. In sagittis tortor et diam feugiat fermentum. Nunc justo ligula, feugiat dignissim consectetur non, tristique vitae enim. Curabitur et cursus velit. Etiam et aliquam urna. Duis pharetra fermentum lectus et fermentum. Phasellus eget nunc ultricies, ornare libero quis, porta justo. Sed euismod, arcu sed tempor venenatis, urna ipsum lacinia eros, ac iaculis leo risus ac est. In hac habitasse platea dictumst. Sed tincidunt rutrum elit, ornare posuere lorem tempor quis. Proin tincidunt, lorem ac luctus dictum, dui mi molestie neque, a sagittis purus leo a nunc.
</p><br>
<br>
<b>Add a link to selected text:</b> <input type="text" id="hrefInput" style="border: solid blue 1px" value="http://www.test.com"> <input type="button" id="gobutton" value="GO!"><br>
<span id="errorMsg" style="display:none;">No selected text!</span><br>
<input type="button" id="undoButton" value="Undo">
JavaScript:
var p = document.querySelector('p');
var old = p.innerHTML;
var HrefInput = document.getElementById("hrefInput");
var GoButton = document.getElementById("gobutton");
var UndoButton = document.getElementById("undoButton");
var errorMsg = document.getElementById("errorMsg");
var idCounter=0;
var textSelected=false;
UndoButton.addEventListener('focus', function() {
console.log("Undo button clicked. Default text reloaded.");
restore();
})
GoButton.addEventListener('click', function() {
if(!textSelected){
errorMsg.style.display="inline";
errorMsg.style.color="rgb(166, 0, 0)";
errorMsg.style.fontWeight="bold";
return;
}
console.log("GO button clicked: Link id=a-"+idCounter+" created.");
targetId="a-"+idCounter;
document.getElementById(targetId).setAttribute("href",HrefInput.value);
document.getElementById(targetId).classList.add("createdlink");
document.getElementById(targetId).setAttribute("title",HrefInput.value);
document.getElementById(targetId).classList.remove("highlighted");
textSelected=false;
idCounter++
})
p.addEventListener('focus', function() {
errorMsg.style.display="none";
});
p.addEventListener('mouseup', function() {
textSelected=true;
console.log("Mouseup event in p : Text selected.");
appendanchor(selectText()); // extract the selection
HrefInput.focus(); // FireFox
HrefInput.blur(); // Needs it. Try without, you'll see.
})
function appendanchor(r) { // onmouseup
if (!r) return;
extracted = r.extractContents();
el = document.createElement('a');
el.setAttribute("id", "a-"+idCounter);
el.setAttribute("class", "highlighted");
el.appendChild(extracted);
r.insertNode(el)
}
function selectText() { // onmouseup
if (window.getSelection) {
console.log("window.getSelection");
sel = window.getSelection();
if (sel.getRangeAt && sel.rangeCount) { // Chrome, FF
console.log(sel.getRangeAt(0));
return sel.getRangeAt(0);
}
else{console.log(sel);}
} else if (document.selection && document.selection.createRange) {
console.log("elseif");
return document.selection.createRange();
}
return null;
}
function restore() {
p.innerHTML = old;
textSelected=false;
}
Substituting your selected region with a span element (and coloring that) could be a workaround:
var p = document.querySelector('p');
var old = p.innerHTML;
var input = document.querySelector('input');
p.addEventListener('blur', () => {
highlight(select()); // save the selection
})
p.addEventListener('focus', () => {
restore(); // restore the selection
})
function highlight(r) {
if (!r) return;
var extracted = r.extractContents();
el = document.createElement('span');
el.appendChild(extracted);
r.insertNode(el)
}
function select() {
if (window.getSelection) {
sel = window.getSelection();
if (sel.getRangeAt && sel.rangeCount) {
return sel.getRangeAt(0);
}
} else if (document.selection && document.selection.createRange) {
return document.selection.createRange();
}
return null;
}
function restore() {
p.innerHTML = old;
}
span {
background: tomato;
color:white;
}
<p contenteditable="true" tabindex="0">
Select something up here and click the input below
<br> on firefox the input get the focus and the text still selected.
<br> on chrome the text still selected but the input lose focus
</p>
<input type="text" id="special" style="border: solid blue 1px">
works on chrome, but not on FF
as suggested by Eric using mouseup
event (I actually used blur
) on p
to call highlight(select())
will fix the issue on FF.
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