2

It seems that window.getSelection() is empty until mouseup occurs. I am able to get the clicked word and select it on mouseup, but I need to do this on mousedown (before mouseup occurs). In this jsfiddle example I am triggering mouseup (which is triggered successfully) but the text selection is still empty until the physical mouseup occurs.

$(function() {
    app_init();
});

function app_init() {
    container = $('div');
    selection = false;
    word = false;
    start = false;
    end = false;

    if(window.getSelection) {
        selection = window.getSelection();
        selection.empty();
    } else {
        alert('Please update your browser to use this application.');  
    }

    container.mousedown(function(e) {
        console.log('mousedown');
        mouse_press(e);
    });

    container.mouseup(function(e) {
        console.log('mouseup');
        mouse_release(e);   
    });

}

function mouse_press(e) {
    $(e.target).trigger('mouseup'); // this triggers the mouseup but selection is empty
}

function mouse_release(e) {
    handle_selection(); //physical mouseup works
}

function handle_selection() {
    selection = window.getSelection();
    //console.log(selection);

    if(selection.isCollapsed) {
        // this is how i am selecting the clicked word, and yes i know .modify is non-standard
        selection.modify('move', 'forward', 'character');
        selection.modify('move', 'backward', 'word');
        selection.modify('extend', 'forward', 'word');

        word = selection.toString();
        start = selection.anchorOffset;
        end = selection.focusOffset;

        console.log( 'word:'+word+' start:'+start+' end:'+end );

    }
}

Is there any other way to trigger the text selection (which isCollapsed true) while the mouse is still down?

3 Answers 3

0

Just call window.getSelection() in the mousedown event. But keep in mind that it will return what was selected before the mousedown.

container.mousedown(function(e) {
   selection = window.getSelection();
   console.log('mousedown='+ selection);
   mouse_press(e);
});

$(function() {
	app_init();
});

function app_init() {
	container = $('div');
	selection = false;
  word = false;
  start = false;
  end = false;
  
  if(window.getSelection) {
  	selection = window.getSelection();
    selection.empty();
  } else {
  	alert('Please update your browser to use this application.');  
  }
  
  container.mousedown(function(e) {
  	 $('.result').text($('#word').text());
  });

  container.mouseup(function(e) {
  	mouse_release(e);  	
  });

}

function mouse_press(e) {
	$(e.target).trigger('mouseup');
  //container.trigger('mouseup');
  //handle_selection();
}

function mouse_release(e) {
	handle_selection();
}

function handle_selection() {
	selection = window.getSelection();

  if(selection.isCollapsed) {
    
		selection.modify('move', 'forward', 'character');
		selection.modify('move', 'backward', 'word');
		selection.modify('extend', 'forward', 'word');
    
		word = selection.toString();
    start = selection.anchorOffset;
    end = selection.focusOffset;

    
  }
}

// wrap words in i.ele
$(document).on('mouseenter', 'p',  function() {
    var $this = $(this);
    var word = $this.text().replace(/\b(\w+)\b/g, "<i class='ele'>$1</i>");
    $this.html( word );
});
// unwrap on mouseleave
$(document).on('mouseleave', 'p',  function() {
    $(this).find('i.ele').contents().unwrap().end().end().html()

});

// bind to each span
$(document).on('mouseenter', 'p i.ele',  function() {
    var word = $(this).css('background-color','#ffff66').text();
    $('#word').text(word);
});
 
$(document).on('mouseleave', 'p i.ele',  function() {     
    $('#word').text('');
    $(this).css('background-color',''); 
});
div {
   border: 1px dotted orange;
   padding: 1em;
}
p {
  font-size: 1.5em;
  font-family: sans-serif;
}
.result {
  border: 1px #ccc solid;
  padding: 3px 10px;
  height: 30px;
}
i.ele {
  font-style: normal;
}
#word {
  display: none;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div>
  <p>
    This is some type of testing test.
  </p>
  <p class="result"></p>
  mousedown word: <span id="word"></span>
</div>

Edit: now shows text that is highlighted while dragging the mouse, using mousemove. You may need to expand the snippet to see the result.

Edit 2: Now detects words on hover and captures it on mousedown.

Sign up to request clarification or add additional context in comments.

9 Comments

Thank you but I need to get what is currently being clicked (mousedowned) on, not what was previously selected.
I changed the event to mousemove. It now captures the text that is highlighted while dragging the mouse. Let me know if that works for you.
Thank you again and yes this is much closer to what I am after but it really needs to select the word without relying on the mouse movement. There must be some way to trigger it after mousedown alone?
I'm not clear on what you want. So the user highlights text, THEN mousedown on it?
I modified the snippet so you can easily see what is is capturing. You could capture the result on an interval and process that. Or you could detect when the mouse stops moving during the mousedown event.
|
0

Problems

  • First, there's no point binding event handlers to both mouse events. The results are the same, only the timing is different.

  • "mousedown" event will fire when the mouse button is clicked down and if there was no previous selection, then nothing will be logged to console. Of course .empty() guarantees that there's never a previous selection.

  • "mouseup" event will fire when the mouse button is released at the end of a click and there's always a selection since the event handler uses .modify().

  • "click" event is both "mousedown" and "mouseup" which is used in the example.

  • The first line is useless:

    /**
     * User clicks the word "test".
     * <p>This is some text to test on.</p>
     *                          ^ collapsed at "e"
     */
    
    selection.modify('move', 'forward', 'character'); // 👎
    /**
     * <p>This is some text to test on.</p>
     *                           ^ collapsed at "s"
     * Was there a point in moving anchor/focus forward a single letter?
     */
    
    selection.modify('move', 'backward', 'word');             
    /**
     * <p>This is some text to test on.</p>
     *                         ^ collapsed at "t"
     */
    
    selection.modify('extend', 'forward', 'word');
    /**
     *                             ⌄ focus at " "
     * <p>This is some text to test on.</p>
     *                         ^ anchor at "t"
     */
    
  • Defining variables without a keyword var, let, or const is fundamentally bad. I recommend the following:

     // Define "container" since it's not going to change.
     const container = $('div');
     // Declare (not define) these variables and define them later.
     let selection, word, start, end;
    

Example

FIDDLE

Details are commented in example.

/**
 * Bind the "click" event to the Document Object.
 * Any <p> is "this".
 * Call event handler getWord(e) when "click" is fired.
 */
$(document).on("click", "p", getWord);

// Event handler passes (e)vent Object by default.
function getWord(e) {
  // Declare variables with keyword "let" 👍 or "var" 👎
  let chars, start, close;
  // Define the Select Object.
  const select = window.getSelection();

  // If the user clicked an element that has text...
  if ($(e.target).text().trim().length) {
    /**
     * ...move selection back to the beginning of the word where
     * the "click" originated. The selection is collapsed
     * so the anchorOffset and focusOffset are the same.
     * This is like a "mousedown" where nothing is selected.
     */
    select.modify('move', 'backward', 'word');
    /**
     * Next the selection is stretched to the end of the space
     * that follows the word. anchorOffset and focusOffset
     * are set since selection consists of text from word.
     * This is like a "mouseup".
     */
    select.modify('extend', 'forward', 'word');

    chars = select.toString();
    start = select.anchorOffset;
    close = select.focusOffset;

    console.log(`word: ${chars} start: ${start} close: ${close}`);
    // Clear selection of any ranges.
    select.empty();
  }
}
:root {
  font: 3ch/1.15 "Segoe UI", sans-serif;
}

body {
  display: flex;
  justify-content: center;
}


/**
 * For demo purposes.
 */
.as-console-wrapper {
  pointer-events: none;
}

.as-console-row {
  visibility: collapse;
}

.as-console-row:last-of-type {
  visibility: visible;
}
<p>This is some text to test on.</p>

<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.4.0/jquery.min.js"></script>

Comments

0

Use the 'selectionchange' event on the document

like so:

document.addEventListener('selectionchange', handle_selection);

I think this does what you were intending: https://jsfiddle.net/2j9uwpg0/

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.