Get multiple elements from document.getSelection() - javascript

How can I get the multiple elements a user has selected from document.getSelection()?
document.getElementById("hello").onclick = function() {
selection = document.getSelection();
if(selection) {
console.log(selection.anchorNode.textContent);
}
};
http://jsbin.com/qisawudofa/edit?html,js,console,output
It seems to only return the element that was selected first, but in my case I need to get all of them.
Alternatively, is there a way to at least know when multiple elements have been selected?

You're probably most interested in the Ranges that make up the selection. Remember the user can make multiple selections all over the page. Each continuous area of selection will get its own instance of Range.
You'll need to iterate over all of the ranges. For each of them you can see where it starts and where it ends:
if (selection) {
for (i=0; i<selection.rangeCount; i++) {
range = selection.getRangeAt(i);
if (range) {
console.log(range.startContainer);
console.log(range.endContainer);
}
}
}
But for the example described in your code you'll need to consider two more things:
Only if the user very accurately selects a paragraph will you get the paragraph's node in startContainer. They might start their start selection even one character after the beginning of the paragraph and then you'll get a text node with the paragraph as its parent.
The Range only gives you the start and the end of the selection for that range. It doesn't directly give you all of the elements in between. So if the user selects more than 2 paragraphs, you'll need to figure out exactly which paragraphs are between start and end yourself.

Related

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.

Repeat Control using jQuery

I'll probably lose reputation for asking this; but I've been trying infinite variations of code, and failing every time. So I'm reaching out.
I'm working on an aspx. It's all built, they just want some additional functionality.
We're using ScriptSharp to trans-compile to JavaScript.
Basically, we've got an HTML table. Each row in the table represents an invoice. One column in the table represents the amount due (call it amountDue) on the invoice. Another column on the table contains a textbox wherein the user may enter the amount to apply to the invoice (call it amountToPay). If the amounts differ, another column populates with a textbox, pre-populated with the difference between the amount due and the amount entered (call it difference). Following this column is another column with a drop-down list of reasons to explain the discrepancy (call it reason). The user may change the difference in the difference textbox. If that happens, a new additional difference textbox and a new additional reason drop-down list need to appear on the same row of the table, each under its appropriate column.
My first attempts duplicated controls geometrically, for example, going from two difference textboxes to eight. I figured that out.
Now every combination of jQuery functions I try either duplicates all the controls, or none of the controls. So, on difference change, either no new difference textbox is added, or the number of difference textboxes that exist is added. So if two exist, four result. If four exist, eight result.
Okay, here's some code.
Here are the two columns for difference and reason.
<td class="currency">
<div>
<input class="difference_textbox" type="text" value="0.00" style="display: none;" />
</div>
</td>
<td>
<div>
<select style="display: none;" class="adj_reason_select">
<option></option>
</select>
</div>
</td>
I'll skip the ScriptSharp and just list the trans-compiled JavaScript (debug version):
// Let this function represent the function called on `difference` change.
ReceivePayment._addAdjustment = function ReceivePayment$_addAdjustment(e) {
var self = $(e.target);
var customerInvoice = self.parents('.customer_invoice');
var amountPaidBox = customerInvoice.find('.amount_to_pay_input');
// ...
var amountPaidTD = amountPaidBox.closest('td');
var diffTextBoxTD = ReceivePayment._duplicateInputControl(amountPaidTD);
var adjReasonSelectTD = ReceivePayment._duplicateInputControl(diffTextBoxTD);
// ...
}
ReceivePayment._duplicateInputControl = function ReceivePayment$_duplicateInputControl(td) {
// This is very verbose so that I can stop at any point and
// examine what I've got.
var o = td.next(); // Grab the next td.
var divs = o.children(); // Grab the div(s) contained within the td.
var div = divs.last(); // Grab the last div within the td.
// And here's where all my gyrations occur, infinite permutations
// of jQuery calls, not one permutation of which has succeeded in
// adding the contents of the final div to the list of divs.
var d = div[0];
var html = d.outerHTML;
var s = html.toString();
div.add(s);
return o;
}
My attempts include calling after, insertAfter, html, clone, cloneNode, appendChild, and on, and on, on different objects, including divs, div, o, etc.
Part of my problem is that I've not worked with jQuery much. I know just enough to be dangerous. But surely this is possible. Given a td, find the following td. Within that td will be a list of one or more divs. Get the last of those divs, copy it, and append that copy to the list of divs. Done.
What, oh what, am I missing? Flame on.
I appear to have stumbled upon the solution after a shameful amount of time spent spinning my wheels. I gave up. Then, of course, it hit me:
ReceivePayment._duplicateInputControl = function ReceivePayment$_duplicateInputControl(td) {
// This is very verbose so that I can stop at any point and
// examine what I've got.
var o = td.next(); // Grab the next td.
var divs = o.children(); // Grab the div(s) contained within the td.
var div = divs.last(); // Grab the last div within the td.
o.append(div.clone());
return o;
}

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