check if value changed upon change() trigger - javascript

I noticed that change() can be triggered for a <select> even if the option was not changed.
See my jsfiddle example.
Changing the selection will change the text input displayed. However (and I do know it's a bit of stretch) if you:
select the drop down by clicking on its label Selection:
press down on the keyboard (assuming Show A was selected)
then select Show A with the mouse pointer it will trigger change().
Is there an easy workaround for this (right now I'm thinking of using a variable to keep track what the last selection was upon change())?
EDIT:
It seems it is a Chrome-specific problem. I was able to fix the problem by using a variable to keep track of what the last selected item was. I guess this a bug left for the jQuery/Chrome developers.

I would store the current value as data on the element (rather than a variable) like this:
$('#variable').data('currentval', $('#variable').val()).change(function() {
var t = $(this);
if (t.data('currentval') != t.val()) {
$(this).data('currentval', $(this).val());
$('#listA').toggle('fast');
$('#listB').toggle('fast');
}
});
http://jsfiddle.net/emMx6/2/

Related

How can I detect when a user has selected an option from a datalist?

I want to be able to detect when a user has selected an option from the dropdown box on an input element linked to a datalist so that I may use that action to trigger another.
For some reason, clicking on the datalist dropdown box does not fire a click event. So using that is totally out of the question.
Using the change event doesn't work for two reasons. Chrome's change event is pretty nice, it will fire when someone selects an option, however, in Firefox the event is not fired until the input has lost focus. You may think that you could at least use the change event for Chrome, but alas, there is another major issue—if you type in the full text of what you're selecting and then click an option, the change event is never fired, because nothing changed :(
Finally, we come to the very ugly but seemingly only option: comparing the current value of the input element to the options in the datalist with an input event. This has many issues.
It doesn't work if one option is a prefix of another (e.g. foo, foobar). The code will run prematurely if the user types foo before selecting foobar.
Assuming none of your options are prefixes of each other, your code will still run if the user types out the whole text of something in the datalist, as opposed to waiting for them to select it.
It has to loop through every single option in the datalist every time you
type a character.
You could try to mitigate these issues using setTimeout to detect when someone has stopped typing, but that still wouldn't be able to achieve the desired behaviour.
I can't believe there's not a simple event that deals with this, but I know there must be a way.
Indeed, there is a way! Our hero is the Invisible Separator (U+2063). It is a character that adds no visible effect to a string, it merely exists. Copy and paste this f⁣oo and check its length. You will see it equals 4!
We can append this character to the end of every option in the datalist. Then we can set up a listener for an input event, and check if the value of the input element ends in U+2063. If it does, you know the user has selected an option. You then should change the value of the input element to get rid of the U+2063 (unless the selection is going to trigger something that resets the value anyway).
This overcomes every issue you've stated because the user can't actually type in anything that matches something in the datalist, an option must be selected for anything to happen.
document.querySelector('input').addEventListener('input', function(){
if (this.value.slice(-1) === '\u2063') {
this.value = this.value.slice(0, -1);
let div = document.querySelector('div');
div.textContent = `you selected: ${this.value}`
div.classList.toggle('red'); //so you can see when this is called even if the text doesn't change
}
});
body {
display: flex;
}
div {
margin-left: 1em;
}
.red {
color: red;
}
<input list='test'>
<datalist id='test'>
<option>foo⁣</option>
<option>bar⁣</option>
<option>foobar⁣</option>
</datalist>
<div></div>

jQuery: generate Select element

I have a table with data, and when I click on a cell in a certain column, I want it to change into a select dropdown for the user to choose a category for that row (which will be written to the database by AJAX but that'll come later).
I've done something similar before with text boxes using this, which works great, but I'm not sure if I'm modifying it correctly.
I've created a JSFiddle which shows the problem I'm having. I click on the text and it turns into a select element as expected, but when I click on that to choose an option, the dropdown doesn't stay open and I can't select anything. Debugging has shown me that when I click the dropdown, it runs the $("td.ChooseType").click() routine again so I've tried to suppress that by removing the class then adding it back on on selection, but that hasn't solved it. On the rare occasion that the dropdown stays open, I am unable to select anything by either mouse or keyboard.
All of the users will be on IE8 unfortunately, so I need it to be compatible with that.
Thanks!
You need to use event delegation, as otherwise that click event is always bound to that td - regardless of whether its class changes.
Simply change:
$("td.ChooseType").click(function() {
To:
$("table").on('click', '.ChooseType', function () {
JSFiddle demo.
Purely as an alternative to the accepted answer, you can remove an attached handler with unbind. So instead of adding and removing the class, you could unbind and rebind your handler. Only requirement is that the function can't be in-line, but has to be declared separately.
example: http://jsbin.com/qiqunici/1/edit
var handler = function () {
$(this).unbind('click', handler); //unbind the clicked element only
//create and change the element
//inside the select-change event, instead of addClass, re-attach:
{
//$(this).parent().addClass("ChooseType").text(selected).find('select').remove();
$(this).parent().click(handler).text(selected).find('select').remove();
}
};
$("td.ChooseType").click(handler);

How to prevent deselection of selected text on blur (focus lost) in html

I have been doing research on this simple sounding issue for a couple of days and I have not seen any result.
In a nutshell my problem is as follows: I would like to select text in a some input field, move focus to another field (or generally speaking some other element), but not lose my selected text.
Such a situation could correspond to a use-case in which I select text in a field, right-click and display a custom popup menu, but do not wish to lose focus of selected text, because I want to do some operations on the previously selected text.
A small code test sample would be (for my initial simple scenario - here I am forcing text selection when the second input field gains focus):
<html>
<head>
<script src="http://code.jquery.com/jquery-latest.js"></script>
</head>
<body>
<input type="text" id="text1" size="20" value="Test1"/>
<input type="text" id="text2" size="20" value="Test2"/>
<script>
$('#text2').focus( function (evt) {
var target = $('#text1')[0];
target.select();
console.log('active/focused element: ' + document.activeElement.id);
});
</script>
</body>
</html>
I have been searching SO and web for a solution to this and have not seen much if any help.
I am not sure this is even really possible (due to the link between blur and selection lost and focus and selection). I have seen a style property called preventDeselect, in another SO answer - this does not work and I have not even such documentation or browser support for this.
I am quite struggling with this and would appreciate some help: even saying I can't do this at all or maybe some ways to go.
UPDATE:
Just for the record, my user scenario, which refers to text selection and context menu, is a common one (it slipped my mind to mention): just select some text in this page (or in an input type field) and right click to get the browser's default context menu - my scenario is different in that i want to use a custom menu, but with similar behavior to the browser's context menu - which normally allows to select some text, cut/copy the selection, navigate within the context menu without losing the selected text. So I think it should be possible somehow :) to do all these things with a context menu and still have your selection.
Attempting to answer this part of your question:
Such a situation could correspond to a use-case in which I select text
in a field, right-click and display a custom popup menu, but do not
wish to lose focus of selected text, because I want to do some
operations on the previously selected text.
For this use-case, I created a quick fiddle: http://jsfiddle.net/4XE9a/1/
Note: Am using the same getSelection function from #David's answer.
If you select any text and then right-click on the input, a custom popup menu appears. Click "option 1". You will find that the selection is not lost even though the focus has shifted to that anchor tag.
However, for the second part of your question regarding focus shifting to another textbox, #David's answer suffices.
Update: (after your comments)
Please see this updated fiddle: http://jsfiddle.net/783mA/1/
Now, when you select some text and right-click on the input it will show the custom popup menu with three options. Use tab to navigate and press space or click on the highlighted option. (Due to paucity of time I could not implement up/down arrow keys, but the concept remains the same)
This demonstrates your question in the comment that the selection is still not lost while navigating the menu.
Note: You are wanting to visually keep the selection highlight and not lose the selection while clicking anywhere else. Please note that this is not possible because text selection behavior is OS implemented. Browser, html etc do not play a role here. The text selection is lost as soon as you click anywhere outside the context of selection. This is because the system starts expecting a new selection as soon as you click anywhere outside. However, controls without text surface are exempt. Button, scrollbar arrows etc will not cause selection to lose.
To view this behaviour, in the fiddle, select some text and then click any dropdown on the left pane. The text selection is not lost, even visually for that matter.
This is why in the new fiddle above, I purposely used buttons to demonstrate.
You can save each selection in an interval, then retrieve it when you like. Here is an example that pulls the selection when the input has focus and clears the interval on blur:
function getSelection(elm) {
var start = elm.selectionStart;
var end = elm.selectionEnd;
return elm.value.substring(start, end);
}
$('input').focus(function() {
var self = this;
$(this).data('interval', setInterval(function() {
$(self).data('selection', getSelection(self));
},20));
}).blur(function() {
clearInterval($(this).data('interval'));
});
Now you can stuff like:
$('#text2').focus(function() {
console.log('selection in #text1 was: '+$('#text1').data('selection'));
});
Demo: http://jsfiddle.net/qCCY5/

javascript select input previous selection

I don't have much experience with javascript so I' not sure if/how this can be done. So I have a number of select components with a couple of options. One of these options for all of the components is "All".
Now I need to make sure that no more than one component has "All" selected. I'm using some input fields and making some validations to make sure this condition stands. However I can't see how I can do the following. When a user selects "All" from a field, and this is not allowed because there is already an "All" on the page, put the previous selection in it's place.
So far what I have is (component being the current select component):
selectedName = component.options[component.selectedIndex].innerHTML;
if (selectedName == 'All') {
if (emptyAllSpace() == 1) {
//here I do all the mumbo jumbo needed and assign to the hidden field the name of
//the component
}
else {
//Here is where I should put back the old selection value.
}
}
Now the else branch is where I need to figure something out. So how can I / how difficult would it be to get the selected option before the currently selected one.
I'm also interested how difficult it would be because I could also go another route and just put as selected the first option that is not 'All' but I would rather avoid this if it's no too time consuming.
Regards,
Bogdan
Use the onchange event for the select. First store the default value, and reassign it on change. Or use 2 variables which keep the last 2 selected options(?).
UPDATE
Use the onfocus to get the value before it changes, http://jsfiddle.net/gQHqj/ .
In this handler you could register the following onchange, and have that value stored in the closure (onfocus's scope object).
UPDATE 2
I knew something was a bit suspicious, but didn't have time to inspect, if the user doesn't move to another element or click somewhere the element is still in focus, and the onfocus will not be called, while onchange will (if the user changes the value), with the wrong prev value. So I came up with a bugfix, you should have this structure : http://jsfiddle.net/gQHqj/4/
You could keep the previously selected value in a variable. This variable's value would be populated on form/page load and update only in case the selection is correct. Otherwise, you will revert the selection to its value.

how to create a new text field below when pressing a key?

last.fm has this nifty feature when you're adding an event. you have the Artists field, and when you start typing in it, another text field appears beneath it. when you start typing in that next field, another new field appears beneath and so on. I've been trying to figure out how to mimic this functionality using jquery but I can make it work on the first, stationary, field only. any ideas?
edit:
actually, nope, it's not working even for the stationary one, since it adds the field on EVERY key press
edit2:
alrighty, so some fine folks have already solved my adding issue, now, how would one go about adding the field only after the first time a key is pressed.
You are probably only binding your handler to the first static input, and not the dynamically created ones. Use .live() to do your event binding. That will bind the event to future elements that match the selector.
To make sure you only add one new one, make sure you only add it when typing into the last textbox. Check $(this).closest(".container").next(".container").length to make sure there isn't already a new textbox.
$("input.myClass").live("keyup", function (e) {
var $container = $(this).closest(".container");
if (this.value && !$container.next(".container").length) {
var $newContainer $("<div>").insertAfter($container).addClass("container");
...
}
});
Live is the right idea but closest is overkill: jsFiddle
$("input").live("keyup", function (){
if ( !$(this).next('input').length ) $(this).after('<input type="text"></input>');
});

Categories

Resources