I am building a JS script which at some point is able to, on a given page, allow the user to click on any word and store this word in a variable.
I have one solution which is pretty ugly and involves class-parsing using jQuery:
I first parse the entire html, split everything on each space " "
, and re-append everything wrapped in a <span class="word">word</span>
, and then I add an event with jQ to detect clicks on such a class, and using $(this).innerHTML I get the clicked word.
This is slow and ugly in so many ways and I was hoping that someone knows of another way to achieve this.
PS: I might consider running it as a browser extension, so if it doesn't sound possible with mere JS, and if you know a browser API that would allow that, feel free to mention it !
A possible owrkaround would be to get the user to highlight the word instead of clicking it, but I would really love to be able to achieve the same thing with only a click !
Here's a solution that will work without adding tons of spans to the document (works on Webkit and Mozilla and IE9+):
https://jsfiddle.net/Vap7C/15/
$(".clickable").click(function(e){
s = window.getSelection();
var range = s.getRangeAt(0);
var node = s.anchorNode;
// Find starting point
while(range.toString().indexOf(' ') != 0) {
range.setStart(node,(range.startOffset -1));
}
range.setStart(node, range.startOffset +1);
// Find ending point
do{
range.setEnd(node,range.endOffset + 1);
}while(range.toString().indexOf(' ') == -1 && range.toString().trim() != '');
// Alert result
var str = range.toString().trim();
alert(str);
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<p class="clickable">
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Mauris rutrum ante nunc. Proin sit amet sem purus. Aliquam malesuada egestas metus, vel ornare purus sollicitudin at. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer porta turpis ut mi pharetra rhoncus. Ut accumsan, leo quis hendrerit luctus, purus nunc suscipit libero, sit amet lacinia turpis neque gravida sapien. Nulla facilisis neque sit amet lacus ornare consectetur non ac massa. In purus quam, imperdiet eget tempor eu, consectetur eget turpis. Curabitur mauris neque, venenatis a sollicitudin consectetur, hendrerit in arcu.
</p>
in IE8, it has problems because of getSelection. This link ( Is there a cross-browser solution for getSelection()? ) may help with those issues. I haven't tested on Opera.
I used https://jsfiddle.net/Vap7C/1/ from a similar question as a starting point. It used the Selection.modify function:
s.modify('extend','forward','word');
s.modify('extend','backward','word');
Unfortunately they don't always get the whole word. As a workaround, I got the Range for the selection and added two loops to find the word boundaries. The first one keeps adding characters to the word until it reaches a space. the second loop goes to the end of the word until it reaches a space.
This will also grab any punctuation at the end of the word, so make sure you trim that out if you need to.
As far as I know, adding a span
for each word is the only way to do this.
You might consider using Lettering.js, which handles the splitting for you. Though this won't really impact performance, unless your "splitting code" is inefficient.
Then, instead of binding .click()
to every span
, it would be more efficient to bind a single .click()
to the container of the span
s, and check event.target
to see which span
has been clicked.
Here are improvements for the accepted answer:
$(".clickable").click(function (e) {
var selection = window.getSelection();
if (!selection || selection.rangeCount < 1) return true;
var range = selection.getRangeAt(0);
var node = selection.anchorNode;
var word_regexp = /^\w*$/;
// Extend the range backward until it matches word beginning
while ((range.startOffset > 0) && range.toString().match(word_regexp)) {
range.setStart(node, (range.startOffset - 1));
}
// Restore the valid word match after overshooting
if (!range.toString().match(word_regexp)) {
range.setStart(node, range.startOffset + 1);
}
// Extend the range forward until it matches word ending
while ((range.endOffset < node.length) && range.toString().match(word_regexp)) {
range.setEnd(node, range.endOffset + 1);
}
// Restore the valid word match after overshooting
if (!range.toString().match(word_regexp)) {
range.setEnd(node, range.endOffset - 1);
}
var word = range.toString();
});
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