On Text Highlight Event?
Asked Answered
L

10

87

I'm curious if anyone knows how I would trigger a function to run if/once the user finishes selecting text on the web page? I would like the user to be able to select text, and after a short delay(or immediately, at this point it doesn't matter much) an overlay button appears near the text that the user can then click and I go back and run more of my code that is based on the selection. This is for a Firefox extension.

A similar example that I can think of would be like in IE where you can select text and then it brings up the "web accelerators". I'm 99% sure I know how I would actually overlay the button, and get the position of the selected text, but I have no idea how to check to see if there is anything selected, without doing some sort of infinite loop, which just seems like a terrible idea.

EDIT:

//In my overlay.js with the rest of my sidebar code
isTextSelected: function () {   
        var myText = cqsearch.getSelectedText();
        var sidebar = document.getElementById("sidebar");
        var sidebarDoc = sidebar.contentDocument || document;

        var curHighlightedDiv = sidebarDoc.getElementById("testDiv");
        curHighlightedDiv.innerHTML = "Current text selection:" + myText;
    }
};

//In my on firefox load function I added this
document.onmouseup = cqsearch.isTextSelected;

So this is what I have come up with using Robert's suggestion, and it took me some time getting everything in the right spot, but it works great! Now on to position my button.

Lipscomb answered 16/9, 2010 at 22:26 Comment(1)
Take a look at .select() - api.jquery.com/selectPremolar
L
97

There isn't any onhighlightext or anything like that, but a solution would be to bind onmouseup to check if any text is selected if this isn't in a input/textarea using the document.selection Selection API

Edit

Here's an implementation example for you. I only tested this in Chrome/Firefox/IE7. This works in inputs as well.

http://jsfiddle.net/qY7gE/

Code from JSFiddle:

var t = '';
function gText(e) {
    t = (document.all) ? document.selection.createRange().text : document.getSelection();

    document.getElementById('input').value = t;
}

document.onmouseup = gText;
if (!document.all) document.captureEvents(Event.MOUSEUP);
<input type='text' id='input' />
In software, a stack overflow occurs when too much memory is used on the call stack. The call stack contains a limited amount of memory, often determined at the start of the program. The size of the call stack depends on many factors, including the programming language, machine architecture, multi-threading, and amount of available memory. When too much memory is used on the call stack the stack is said to overflow, typically resulting in a program crash.[1] This class of software bug is usually caused by one of two types of programming errors.[2]
Lavine answered 16/9, 2010 at 22:33 Comment(7)
What objects 'onmouseup' How would I assign to? something like, window.onmouseup = myFunction(); ?Lipscomb
Whatever you want to monitor, if you want to monitor the entire window, use window.onmouseupLavine
A disadvantage of this approach is that it doesn't work when a user selects text using the keyboard (Shift+→, etc.).Moen
@Marcel Korpel: In some sort of textarea or input, yes, but you could use the onselect event for those.Lavine
@Robert, fantastic this sounds like it would work really well! I'm going to try to implement it, and will end up marking as answered later today, early tmrw. Thanks! @Marcel I'm not sure if I need it, so this should work well. Thanks for the heads up though!Lipscomb
Note that keyboard selection is also possible for standard body text, not just text inputs.Bona
> "There isn't any onhighlightext or anything like that"..........Yes, there is: selectionchange eventDelanie
R
36

There is a native event for when a text selection is made/changed. selectionchange has basic support on most browsers, including IE, and will work for any text within a document not just form elements.

document.addEventListener("selectionchange",event=>{
  let selection = document.getSelection ? document.getSelection().toString() :  document.selection.createRange().toString() ;
  console.log(selection);
})
select this text

Note, as its name implies it fires on any change of selection. So you will get multiple calls to your callback function as you select text.

Roter answered 4/9, 2018 at 2:21 Comment(2)
I actually think listening to mouseup event is better than selectionchange as the latter fires so many events (up to the chars selected), you have to wait some arbitrary period to get the final selection.Connotative
There's such a thing as debouncing.Visualize
V
15

A bit late to the party but for future reference...

Take a look at the select DOM event on MDN.

It fires once the mouse or key is released (at least in Chrome 40).

document.addEventListener('select', callback);

Valerie answered 10/2, 2015 at 6:13 Comment(1)
This is a nice event, but it only works inside text inputs and textareas, right?Smallpox
R
9

I think @patrick-evans had the right answer. It's easily the most forward-thinking and API supported answer -- you just need to debounce the event to stop the deluge.

I'm not able to post a reply, but consider this

function debounce(fn, delay) {
  let timer = null;
  return function () {
    var context = this, args = arguments;
    clearTimeout(timer);
    timer = setTimeout(function () {
      fn.apply(context, args);
    }, delay);
  };
};

document.addEventListener("selectionchange", debounce(function (event) {
  let selection = document.getSelection ? document.getSelection().toString() :  document.selection.createRange().toString() ;
  console.log(selection);
}, 250));
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ad est veniam facere culpa expedita optio iste labore doloremque autem illo, in voluptatibus error ea, ab reprehenderit placeat facilis animi iure?
Ruppert answered 10/5, 2019 at 3:49 Comment(3)
This is great! How would we get it to do it only inside a specific element?Jiggle
This answer with debounce deserve more votes. This is a more practical solution. :) Thanks!Gosselin
@Jiggle that would require one to check on the event that the element matches, simplest would be to give the element a unique id="some_id" attribute and check if the ever.currentTarget.id is the sameVisualize
C
2

I'd suggest listening to mouseup event rather than selectionchange as the latter fires quite many events (up to the chars selected), you have to wait some arbitrary period to get the final selection. Ditto @Robert and @Makyen, I've created some code for you to play:

<!DOCTYPE html>
<html>
<body>
  <div onmouseup="showSelection()">
    <p>Select some of the text. You can also listen to the mouseup event at the &lt;p&gt; level</p>
    <p>Anthoer paragraph and text</p>
    <input type="text" value="Hello world!" onselect="showSelection()">
  </div>
  Outside of div, selection won't work as there is no listener if you don't uncomment the line: document.onmouseup = showSelection
  
  <script>
  // document.onmouseup = showSelection // listen to the mouseup event at document level
  
  function showSelection() {
    console.log('Selection object:', window.getSelection()) // same as document.getSelection()
    console.log('Selected text:', window.getSelection().toString())
  }
  </script>
</body>
</html>
Connotative answered 5/3, 2019 at 23:2 Comment(0)
N
2

You could combine the mouseup and selectionchange events to create your own events: highlightstart and highlightend

Try it on Codepen

let state = "hle";
const highlightend = new CustomEvent("highlightend", {bubbles: true}); 
const highlightstart = new CustomEvent("highlightstart", {bubbles: true});
const highlight_start_initiate = () => {
  const mousedown_promise = new Promise((resolve, reject) => {
    document.addEventListener("mousedown", (e) => resolve(e.target)); 
  });
  const selectionchange_promise = new Promise((resolve, reject) => {
    document.addEventListener("selectionchange", (e) => {
      if (window.getSelection().toString().length > 0) {
        resolve();
      }
    });
  })
  Promise.all([mousedown_promise, selectionchange_promise]).then((data) => {
    if (state === "hle") data[0].dispatchEvent(highlightstart);
    state = "hls";
  })
}
const mouseup_event_listener = document.addEventListener("mouseup", (e) => {
  if (state === "hls") e.target.dispatchEvent(highlightend);
  highlight_start_initiate();
  state = "hle";
});
highlight_start_initiate();

Then call them with

document.body.addEventListener("highlightstart", () => {
  console.log("Highlight started");
});
document.body.addEventListener("highlightend", () => {
  console.log("Highlight ended");
});
Negris answered 13/4, 2023 at 7:26 Comment(0)
D
1

You can use selection change event for this

    document.addEventListener('selectionchange', (e)=>{
        console.log("Archor node - ",window.getSelection().toString());
    });


Decussate answered 24/7, 2021 at 14:7 Comment(0)
G
0

The solution using the mouseup trick is not the proper solution. That is a hacky way and not perfect. Less efficient too as you are now catching mouseups for so much crap.

The real way to do it in Firefox addon is to use addSelectionListener see this topic: Observe for highlight?

Now even if user uses keyboard to make selections it is caught.

Credit to Neil for tipping me off on where to find it on MXR

Gorgon answered 30/3, 2014 at 10:27 Comment(2)
Is this just the way to do it in firefox? also MXR is now DXR, not that I know much about this, but is this still relevant?Carboniferous
This is no longer the way. They now use something else I remember reading about it.Gorgon
B
0

I think using mouseup is not a proper solution because we can use keyboard to select the text. selectionchange event + debounce function is a good way to go imho.

Behm answered 19/8, 2023 at 18:24 Comment(0)
J
0

if anyone comes to here and Robert's answer didn't work, here is a new and working example:

var t = '';
function gText(e) {
    t = document.getSelection().toString();

    console.log(t)
}

document.addEventListener("mouseup", gText);
Jollity answered 9/6 at 15:53 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.