I am working on a bookmark app where i have to store the user's selected keywords or words or content. I am using the createRange() and addRange() javascript methods to create the range and then find out the selected elements/contents by the user. The code i written for this is as follow.
<head>
<script type="text/javascript">
var storedSelections = [];
function StoreSelection () {
if (window.getSelection) {
var currSelection = window.getSelection ();
for (var i = 0; i < currSelection.rangeCount; i++) {
storedSelections.push (currSelection.getRangeAt (i));
}
currSelection.removeAllRanges ();
} else {
alert ("Your browser does not support this example!");
}
}
function ClearStoredSelections () {
storedSelections.splice (0, storedSelections.length);
}
function ShowStoredSelections () {
if (window.getSelection) {
var currSelection = window.getSelection ();
currSelection.removeAllRanges ();
for (var i = 0; i < storedSelections.length; i++) {
currSelection.addRange (storedSelections[i]);
}
} else {
alert ("Your browser does not support this example!");
}
}
</script>
</head>
<body>
Select some content on this page and use the buttons below.<br /> <br />
<button onclick="StoreSelection ();">Store the selection</button>
<button onclick="ClearStoredSelections ();">Clear stored selections
</button>
<button onclick="ShowStoredSelections ();">Show stored selections</button>
</body>
This code is working perfectly on Firefox. I am able to select multiple things one by one and able to show the selected content again but on chrome and chromium i am getting Discontiguous selection is not supported.
error when i store more than one elements in range array and click on show stored selections button.
Help will be appreciated. And please suggest me if there is some other alternatives do accomplish this bookmarking task.
Write
window.getSelection().removeAllRanges();
immediately before creating range.
https://bugs.chromium.org/p/chromium/issues/detail?id=399791
Here's the only possible way of doing this that I was able to come up with:
Wrap the selection in <span style="background: Highlight;">...</span>
.
But note:
window.onmousedown
for that rather than window.onclick
, because onclick
is fired after any button is pressed, so when pressing your "Show stored selections" button, a new selection will be created, thus destroying the one that was supposed to be captured.The following code (fiddle) is the best I was able to do:
var storedSelections = [];
var simulatedSelections = [];
window.onmousedown = clearSimulatedSelections;
function storeSelection()
{
if(window.getSelection)
{
var currSelection = window.getSelection();
for(var i = 0; i < currSelection.rangeCount; i++)
{
storeRecursive(currSelection.getRangeAt(i));
}
currSelection.removeAllRanges();
}
else
{
alert("Your browser does not support this example!");
}
}
function storeRecursive(selection, node, started)
{
node = node || document.body;
started = started || false;
var nodes = node.childNodes;
for(var i = 0; i < nodes.length; i++)
{
if(nodes[i].nodeType == 3)
{
var first = nodes[i] == selection.startContainer;
var last = nodes[i] == selection.endContainer;
if(first)
{
started = true;
}
if(started)
{
var sel = selection.cloneRange();
if(!first)
{
sel.setStartBefore(nodes[i]);
}
if(!last)
{
sel.setEndAfter(nodes[i]);
}
storedSelections.push(sel);
if(last)
{
return false;
}
}
}
else
{
started = storeRecursive(selection, nodes[i], started);
}
}
return started;
}
function clearStoredSelections()
{
storedSelections = [];
}
function showStoredSelections()
{
if(window.getSelection)
{
var currSelection = window.getSelection();
currSelection.removeAllRanges();
for(var i = 0; i < storedSelections.length; i++)
{
var node = document.createElement("span");
node.className = "highlight";
storedSelections[i].surroundContents(node);
simulatedSelections.push(node);
}
}
else
{
alert("Your browser does not support this example!");
}
}
function clearSimulatedSelections()
{
for(var i = 0; i < simulatedSelections.length; i++)
{
var sec = simulatedSelections[i];
var pn = sec.parentNode;
while(sec.firstChild)
{
pn.insertBefore(sec.firstChild, sec);
}
pn.removeChild(sec);
}
simulatedSelections = [];
}
.highlight
{
background: Highlight;
}
Select some content on this page and use the buttons below.<br><br>
<button onclick="storeSelection();">Store the selection</button>
<button onclick="clearStoredSelections();">Clear stored selections</button>
<button onclick="showStoredSelections();">Show stored selections</button>
It works in Firefox, Safari and Chrome, but has the following shortcomings:
However, I doubt that anything better is possible in browsers other than Firefox, but even Firefox has a ticket to drop discontiguous selections.
FYI I was getting a similar error when rolling my own "copy to clipboard" feature. I'm not going to address OP's provided code, but I'll tell you how I fixed it in my own code.
Reproduce:
Expected:
"bar" is outputted.
Actual:
"Discontiguous selection is not supported"
Fix:
Call window.getSelection().removeAllRanges()
at the start of your "copy to clipboard" event handler. "Discontiguous" means "not connected". So my guess is that the browser copies the first range (the node containing "foo"), and then gets angry when you try to select another range that is not next to the first node.
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