Get bounding rectangle of selected text javascript - javascript

I use this code How can I position an element next to user text selection? to get the position of the selected text, but it doesn't work for the selected text inside an input. Sometimes the position is 0.
Is there some universal method for detecting the position of the selected text?
I want to show a tooltip on mouseup or dblclick the selected text.

You can use the following code to get the position of selected text:
var selection = window.getSelection();
var getRange = selection.getRangeAt(0);
getRect = getRange.getBoundingClientRect();

You can use getSelection api.
After selection a text run below code in console.
var selection = window.getSelection()
var baseOffset = selection.baseOffset
var length = selection.focusOffset -selection.baseOffset
var text = selection.focusNode.data.splice(baseOffset, length)

If you just need to get the position where the user doubleclicked, use the following snippet.
$('#thatInput').on('dblclick', function (e) {
alert('Position X: ' + e.clientX + '\nPosition Y: ' + e.clientY);
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<input id="thatInput">

The question is about getting the position of mouse pointer when selecting text. I am trying a solution that also works with keyboard events (choosed keyup).
I wrote a sample html page with a "vanilla" script inside to test the capabilities of the Selection API. This is the idea:
When selecting on text nodes, getting the position of selected text is done by means of the Range Object.
But when the selected text is a part of an Input Element, using the getBoundingClientRect() of the Range Object does not work (gave me a full zero ClientRect Object.
So, the function getSel() will try to consider both scenarios: selecting text just from the HTML or inside some input elements (just considered input and textarea).
On the bottom of the page there is a div#results element, for displaying data, then getSel() will create a new div#boundy with the coordinates of the ClientRect object or the related input element coordinates.
I wish to finish it, but I'm out of ideas on how to get the actual position of the selected text inside the input objects. It will have to be adding in a relative way to the coordinates of the element itself.
Answering Andrew, if this works, you'll be able to use the coordinates of div#boundy to place the tooltip wherever you want.
I've created a codepen here.

Related

Selecting element of an contenteditable div when Enter Key is pressed

I want to get the element where the event occured. This snippet only logs the parent element
eg. .
How to log the current element where the key is being pressed
eg. 1st or 2nd
document.getElementById('text').addEventListener('keypress', event => {
console.clear();
console.log(event.target)})
<body>
<div id="text" contenteditable="true">
<div>1st</div>
<div>2nd</div>
</div>
</body>
UPDATE: This should do the trick:
<div id="text" contenteditable="true">
<div>1st</div>
<div>2nd</div>
</div>
<script>
document.getElementById('text').addEventListener('keypress', function(ev) {
var selection = window.getSelection();
var range = selection.getRangeAt(0);
var container = range.commonAncestorContainer;
var nodeParent = container.parentNode;
console.log(nodeParent);
});
</script>
It checks the current selection (the text that is selected by the cursor from start to beginning). In your case, i.e. at keypress, the selection will be the same from start the end, being the actual position of the cursor.
The Selection can contain multiple ranges (all kinds of nodes, including text nodes), depending on what the user selected on the page. Here we are only interested in the first one, where the cursor is, so we do getRangeAt(0).
It then looks at the common ancestor container, being the text that is currently being edited. The commonAncestorContainer is the node in which the Range is situated. In this case it is either the text node with the text "1st" or the text node with the text "2nd".
Now that we have the Text Node that contains the first Range of the current Selection, we can see its parentNode, being the current div.
Does that do the trick?

Swap Divs then swap selected option value between two select lists

The Problem:
Before I began adding the div swaps, I could only type into the left (from_value) input and the result of the calculations would be applied via ajax to the right (to_value) input.
I would like to allow the user to type into either box and have the results display in the opposite box they're typing in.
What I am doing to make this happen:
I am swapping the left div with the right div on mouseover of the to_value input. Here's the code i'm using to do the swap:
$.fn.swapWith = function (that) {
var $this = this;
var $that = $(that);
// create temporary placeholder
var $temp = $("<div>");
// 3-step swap
$this.before($temp);
$that.before($this);
$temp.after($that).remove();
return $this;
};
var leftSide = $('#left-side');
var rightSide = $('#right-side');
$('#to_value_input').on('mouseover', function () {
$(rightSide).swapWith(leftSide);
});
This effectively swaps the divs, bringing along with it ids and names of the inputs which retains functionality of my server-side script to perform calculations. As expected, the from_unit select list and to_unit select list are swapped and their values / displayed text are also swapped from one side to the other.
I would like to swap the values and text of the two select boxes either directly before or more likely, directly after the div swap so it appears as if nothing changed on screen.
Similar questions that I have reviewed:
How to swap values in select lists with jquery?
How to swap selected option in 2 select elements?
I have tried several variations of each of the provided solutions. This seems like it should be a fairly simple thing to do but I find myself stuck. Any further help would be greatly appreciated.
The full code base is available on github if you need to see it: https://github.com/pschudar/measurement-conversion
I have made minor changes to the code hosted there to accommodate the div swaps.

Trigger function on input event if selected text is within input

I've built a page where you can filter results by typing into an input box.
Basic mechanics are:
Start typing, input event is fired, elements without matching text begin hiding
If input becomes empty (or if you click a reset button), all elements are shown again
I have noticed a problem, though, when highlighting text. Say I type "apple" into the input. Then I highlight it, and type "orange."
If an element exists on the page containing "orange," but it was already hidden because I filtered for "apple," it does not show up. I have gathered this is because the input never truly empties; rather, I simply replace "apple" with the "o" from orange before continuing with "r-a-n-g-e." This means I get a subset of "apple" results that contain "orange," as if I had typed "apple orange."
What I really want to do is clear my input on the keypress for the "o" in "orange" before hiding nonmatching elements, so I'm effectively searching the whole page for "orange."
What I've tried so far
1: Set input value to '' on select event:
$('.myinput').on('select', function(){
$(this).val('');
});
This doesn't work because it just deletes my highlighted text, which is unexpected. I only want to reset the input on the keypress following the highlight.
2: Include an if statement in my input event that checks if there is a selection within the input:
$('.myinput').on('input', function(){
var highlightedText = window.getSelection();
if($(highlightedText).parent('.myinput')) {
//reset my input
}
});
This doesn't work because it seems to fire on every keypress, regardless of if there is any actual selection. (Are user inputs always treated as selected?)
3: Add a select event listener to the input element, and set a variable to true if there's a selection. Then, in my input event, check if the variable is true on keypress.
$(function(){
var highlightedText = false;
$('.myinput').on('input', function(){
if(highlightedText = true) {
//reset my input
}
//do stuff
highlightedText = false;
});
$('.myinput').on('select', function(){
highlightedText = true;
});
});
I really thought this one would work because a basic console log in the select function only fires when I want it to – when text in the input is highlighted, but not when other text is highlighted and not when text is entered into the input. But alas, when I change that to a variable toggle, it seems to fire on every keypress again.
So the question is: How can I fire a function on input only if text in my input is highlighted?
I have found this question that suggests binding to the mouseup event, but it seems like overkill to check every single click when I'm only worried about a pretty particular situation. Also, that solution relies on window.getSelection(), which so far isn't working for me.
I've also found another question that suggests to use window.selectionEnd instead of window.getSelection() since I'm working with a text input. I tried incorporating that into option 2 above, but it also seems to fire on every keypress, rather than on highlight.
This answer is not about text selection at all.
But still solve your problem to refilter text when highlighted text is being replaced with new input.
var input = document.getElementById('ok');
var character = document.getElementById('char');
var previousCount = 0;
var currentCount = 0;
input.addEventListener('input', function(){
currentCount = this.value.length;
if (currentCount <= previousCount){
/*
This will detect if you replace the highlighted text into new text.
You can redo the filter here.
*/
console.log('Highlighted text replaced with: ' + this.value);
}
previousCount = currentCount;
char.innerHTML = this.value;
});
<input type="text" id="ok">
<div id="char"></div>
I'll agree with others that you will save yourself some trouble if you change your filtering strategy - I'd say you should filter all content from scratch at each keypress, as opposed to filtering successively the content that remains.
Anyway, to solve your immediate problem, I think you can just get the selection and see if it is empty. You can modify your second attempt:
$('.myinput').on('input', function(){
// get the string representation of the selection
var highlightedText = window.getSelection().toString();
if(highlightedText.length) {
//reset my input
}
});
EDIT
As this solution seems to have various problems, I can suggest another, along the lines of the comment from #Bee157. You can save the old search string and check if the new one has the old as a substring (and if not, reset the display).
var oldSearch = '';
$('.myinput').on('input', function(){
var newSearch = $('.myinput').val();
if (newSearch.indexOf(oldSearch) == -1) {
// reset the display
console.log('RESET');
}
oldSearch = newSearch;
// filter the results...
});
This approach has the added benefit that old results will reappear when you backspace. I tried it in your codepen, and I was able to log 'RESET' at all the appropriate moments.

javascript / jQuery : get selected text's container

How can i get the id of the container (say, id of the p or div) of a user selected text?
I want to make list of selected texts, user selects a text then clicks on a button to add to the list.
And when user clicks the text from the list i want to highlight the place where original selection was made.
I need the id of the container because the selected text may not be unique and appear multiple times in the document.
i get the selected text like this Return HTML from a user-selected text
Here is one way it can be achieved cross browser (untested)
var getSelectionContainer = function() {
if (document.selection){ // IE
return document.selection.createRange().parentElement();
}
var select = window.getSelection();
if (select.rangeCount > 0) {
return select.getRangeAt(0).startContainer.parentNode;
}
};
Demo
(Select some text before 5 Seconds then look in the console)
Links
MDN window.getSelection
MDN selection.getRangeAt
MDN range.startContainer
MDN selection.rangeCount
window.getSelection().anchorNode - Gives you the DOM element where the selection started
window.getSelection().focusNode - Gives you the DOM element where the selection ended
Reference: https://developer.mozilla.org/en-US/docs/Web/API/Selection?redirectlocale=en-US&redirectslug=DOM%2FSelection

How to preserve selection when changing contents of an HTML element

I would like to be able to preserve the users' selection when I change the contents of an HTML element. If the element is updated while the beginning or end of the selection happens to be inside, the entire selection is lost. This also happens while dragging to create a selection, so that if the user is dragging a selection and the element's inner HTML is updated while the cursor is over the element, the user must start over.
I have a <span> that contains a time in the format 'hh:mm:ss am' and is updated each second. The length of the text never changes, so that isn't an issue.
I have tried the following:
var s = window.getSelection();
if (!s.isCollapsed) {
var range = document.createRange();
range.setStart(s.anchorNode,s.anchorOffset);
range.setEnd(s.focusNode,s.focusOffset);
}
document.getElementById('time').innerHTML = new Date().toString();
if (typeof range != 'undefined') { s.removeAllRanges(); s.addRange(range); }
It's the best my research has yielded, but it doesn't seem to make a difference.
What should I do to prevent the selection from vanishing if it happens to start or end in this span?
You need to set the selection again: range.selectNodeContents(newNode);
newNode = document.getElementById("[span id]");

Categories