Reputation: 352
I want to get the user selection then replace them with a mark tag on each text node inside of an element if the user selected two text node which are child, grandchild (for ex: inside a tag which is a child), so surround the first node with <mark>first text node</mark>
and the second one like this <mark><a>partial of second</a></mark><a>rest which is not highlighted</a>
<div>
<p id="first" data-selectable-paragraph="">
At the very first age, JavaScript was called LiveScript. An engineer named Brendan Eich has created JavaScript in 1995. There was a little confused about the name with Java and JavaScript. After several months Microsoft released JScript with Internet Explorer 3. After that Netscape submitted JavaScript to Ecma International. In 1999 ECMAScript edition 3 launched and it has stayed pretty much stable ever since.
</p>
<p>
Another Selection <span> you could do</span>
</p>
<p id="second" data-selectable-paragraph="">
Range is something I discovered recently, which again showed me that the possibilities with the Javascript and the DOM are truly endless. As stated on Mozilla’s developer site, range ‘represents a fragment of a document that can contain nodes and parts of text nodes’. So, if you create of select a section of a document, range could tell you the nodes that it contains, its starting and ending positions relative to the document, it can clone its content ,and much more. (Read more from the docs:
<a href="https://developer.mozilla.org/en-US/docs/Web/API/Range">
https://developer.mozilla.org/en-US/docs/Web/API/Range
</a>)
</p>
</div>
let's say that the user selected 3 texts and highlighted them which I want to result in this structure-> Brief explanation: if the parent or grandparent has data-selectable-paragraph then highlight the text nodes inside the element and its parent if it is the grandchild
<div>
<p id="first" data-selectable-paragraph>
At the very first age, JavaScript was called LiveScript. An engineer named Brendan Eich has created JavaScript in 1995. There was a little confusion about the name with Java and JavaScript. After several months Microsoft released JScript with Internet Explorer 3. After that Netscape submitted JavaScript to Ecma International. In 1999 ECMAScript edition 3 <mark>launched and it has stayed pretty much stable ever since.</mark>
</p>
<p class='second' data-selectable-paragraph>
<mark>Another Selection </mark><mark><span> you could do</span></mark>
</p>
<p id="third" data-selectable-paragraph>
<mark>
Range is something I discovered recently, which again showed me that the possibilities with the Javascript and the DOM are truly endless. As stated on Mozilla’s developer site, range ‘represents a fragment of a document that can contain nodes and parts of text nodes’. So, if you create of select a section of a document, range could tell you the nodes that it contains, its starting and ending positions relative to the document, it can clone its content ,and much more. (Read more from the docs:
</mark>
<mark>
<a href="https://developer.mozilla.org/en-US/docs/Web/API/Range">
https://developer.mozilla.org/en-US/docs/Web/API/Range
</a>
</mark>
<mark>)</mark>
</p>
</div>
what i've found so far was window.getSelection().getRangeAt(0).commonAncestorContainer.hasAttribute("data-selectable-paragraph");
on mouse up but i don't know what to do next
EDIT:
Selected text
its parent is the first P.
its parent is the second P.
its parent is the third P.
There is before after snippets so please take them into consideration
Upvotes: 2
Views: 589
Reputation: 1984
Here is a start. I'm simply allowing you to mark the text once, not set and unset the marks.
window.onload = function () {
var btn = document.getElementsByClassName("get_selection")[0];
btn.addEventListener("click", function () {
var wrap = ["A"]; // nodes that should be wrapped in mark (rather than node's textContent wrapped in mark)
var sel = window.getSelection();
var range = sel.getRangeAt(0);
if (!range.startContainer.isSameNode(range.endContainer)) {
// get all nodes within the range commonAncestorContainer node
var treeWalker = document.createTreeWalker(
range.commonAncestorContainer,
NodeFilter.SHOW_ALL
);
var nodeList = [];
var currentNode = treeWalker.currentNode;
while (currentNode) {
nodeList.push(currentNode);
currentNode = treeWalker.nextNode();
}
var start = null; // index that our selected nodes start
var end = null; // index that our selected nodes end
var selNodes = nodeList.filter(function (val, i) {
// filter the node list
var node = nodeList[i];
start = start ?? (val.isSameNode(range.startContainer) ? i : null); // if same as start node
end = end ?? (val.isSameNode(range.endContainer) ? i : null); // if same as end node
var lesser = start == null || i <= start; // is before start node?
var greater = end != null && i >= end; // is after end node?
return (
!lesser &&
!greater &&
!node.isSameNode(range.endContainer.parentNode) && // node is not same as end node's parent
node != undefined &&
node != null &&
node.textContent.replace(/\t|\n/g, "") != "" &&
node.textContent.replace(/\t|\n/g, "") != undefined &&
!node.contains(range.endContainer) && // node does not contain end node
!node.isSameNode(range.endContainer.parentNode) // node is not same as end node's parent
);
});
// mark node at start of selection
var sParent = range.startContainer.parentNode;
var sText = range.startContainer.textContent;
var mark = document.createElement("mark");
// wrap a tags in mark
if (
wrap.includes(sParent.nodeName) &&
sText.replace(/\t+|\n+/gm, "") ==
sText.substring(range.startOffset).replace(/\t+|\n+/gm, "")
) {
var node = sParent.cloneNode(true);
mark.append(node);
sParent.after(mark);
sParent.remove();
} else {
mark.textContent = sText.substring(range.startOffset);
range.startContainer.textContent = sText.substring(range.startOffset, -1);
range.startContainer.after(mark);
}
// mark node at end of selection
var eParent = range.endContainer.parentNode;
// console.log("end parent: ", eParent);
var eText = range.endContainer.textContent;
var mark = document.createElement("mark");
// wrap a tags in mark
if (
wrap.includes(eParent.nodeName) &&
eText.replace(/\t+|\n+/gm, "") ==
eText.substring(range.endOffset, -1).replace(/\t+|\n+/gm, "")
) {
var node = eParent.cloneNode(true);
mark.append(node);
eParent.after(mark);
eParent.remove();
} else {
mark.textContent = eText.substring(range.endOffset, -1);
range.endContainer.textContent = eText.substring(range.endOffset);
range.endContainer.before(mark);
}
// mark nodes in between start and end
selNodes.forEach(function (val, idx) {
var currentNode = selNodes[idx];
var mark = document.createElement("mark");
if (currentNode.nodeType === Node.TEXT_NODE) {
// if text node, insert mark after node and remove node
mark.textContent = currentNode.textContent;
currentNode.after(mark);
currentNode.remove();
} else {
if (wrap.includes(currentNode.nodeName)) {
var node = currentNode.cloneNode(true);
mark.append(node);
currentNode.after(mark);
currentNode.remove();
} else {
// reset the node's html and append mark
mark.textContent = currentNode.textContent;
currentNode.innerHTML = "";
currentNode.appendChild(mark);
}
}
});
} else {
var parentNode = range.startContainer.parentNode;
var mark = document.createElement("mark");
if (wrap.includes(parentNode.nodeName)) {
var node = parentNode.cloneNode(true);
node.textContent = sel.toString();
mark.append(node);
parentNode.after(mark);
parentNode.remove();
} else {
var sText = document.createTextNode(
range.startContainer.textContent
.substring(range.startOffset, -1)
.toString()
);
var eText = document.createTextNode(
range.endContainer.textContent.substring(range.endOffset).toString()
);
mark.textContent = sel.toString();
range.startContainer.after(eText);
range.startContainer.after(mark);
range.startContainer.after(sText);
range.startContainer.remove();
}
}
});
};
<button class="get_selection">Set Markers</button>
<div>
<p id="first" data-selectable-paragraph>
At the very first age, JavaScript was called LiveScript. An engineer named Brendan Eich has created JavaScript in 1995. There was a little confusion about the name with Java and JavaScript. After several months Microsoft released JScript with Internet
Explorer 3. After that Netscape submitted JavaScript to Ecma International. In 1999 ECMAScript edition 3 launched and it has stayed pretty much stable ever since.
</p>
<p class='second' data-selectable-paragraph>
Another Selection <span> you could do</span>
</p>
<p id="third" data-selectable-paragraph>
Range is something I discovered recently, which again showed me that the possibilities with the Javascript and the DOM are truly endless. As stated on Mozilla’s developer site, range ‘represents a fragment of a document that can contain nodes and parts
of text nodes’. So, if you create of select a section of a document, range could tell you the nodes that it contains, its starting and ending positions relative to the document, it can clone its content ,and much more. (Read more from the docs:
<a href="https://developer.mozilla.org/en-US/docs/Web/API/Range">
https://developer.mozilla.org/en-US/docs/Web/API/Range
</a> )
</p>
</div>
Upvotes: 3