Infinite loop of jQuery focus() event with a <select> element - javascript

I have this HTML:
<select id="select-one">
<option value="">Choose</option>
<option value="1">House</option>
</select>
<select id="select-two">
<option value="">Choose</option>
<option value="A">Table</option>
</select>
And this Javascript with JQuery
$("#select-two").focus( function() {
if( $("#select-one").val() == "" ) {
alert("Fill select-one first!");
return false;
}
});
So i am getting a infinite loop with alerts because after call alert() Javascript puts the focus again in the same select (select-two).
Someone can help me to solve this please?

Note: based on your comments, this assumes you must listen to the focus event.
Solution 1 - using blur() - effective but buggy in Chrome
In theory, the focus event is not cancelable, so return false or event.preventDefault() will have no effect in this case. However, in practice, you can reverse the event by using the blur() method.
For example:
$('#select-two').on('focus',function () {
if ($("#select-one").val() == "") {
$(this).blur();
alert('Fill select-one first!');
return false;
}
});
See jsFiddle demo
This effectively prevents the field from regaining focus after the alert call and so the focus event is not repeated. The only problem is that in Chrome even though the field is not focused anymore, the dropdown remains open (see demo).
Solution 2 - using remove() and clone() - costly but cross-browser
If Chrome's behavior is problematic, you can take a more crude approach, whereby you remove() the select from the DOM, clone() it and then reinsert it into the DOM. This will effectively "reset" the select element completely, leaving it without focus as well as closed.
For example:
$(document).on('focus','#select-two',function (e) {
if ($("#select-one").val() == "") {
$(this).remove().clone().insertAfter('#select-one');
alert('Fill select-one first!');
return false;
}
});
See jsFiddle demo
The upside of this approach is that it works well in Chrome too. The downside of this approach is that it involves manipulating the DOM for a very trivial issue.

I think you need an extra event that change content select-two when the value of select-one has "" like this:
HTML
<select id="select-one">
<option value="">Choose</option>
<option value="1">House</option>
</select>
<select id="select-two">
<option value="">Choose</option>
<option value="A">Table</option>
</select>
JS
$("#select-one").change(function() {
if ($(this).val() == "") {
$("#select-two").val("");
}
});
$("#select-two").focus(function() {
if( $("#select-one option:selected").val() == "" ) {
alert("Fill select-one first!");
$("#select-one").focus();
return false;
}
});
Demo

Related

Select onchange with keyboard - do not trigger onchange event until focusout?

I have a select that is bound to a change event so that it will take the user to a new page when a selection is made. It's fine with the mouse, but when I try to make a selection using my keyboard's arrow keys, the change event fires as soon as I press the arrow rather than waiting for me to tab out, so I can only ever select the first option with my keyboard.
$selectLocation.on('change', function() {
location.href = '/data#' + $(this).val().toUpperCase();
});
How can I differentiate between a click and a keypress on my change function, or otherwise make the change function not fire on keypress?
Consider the following snippet:
// Sets the redirect based on user activity on #test.
$('#test').on('change', function(e) {
if ($(this).data('clicked')) {
// A click was used to change the select box, redirect.
console.log('clicked redirect');
}
});
// Sets data-keypressed on #test when the down or up arrow key is pressed.
$('#test').on('keydown', function(e) {
var code = e.keyCode || e.which;
if (code === 38 || code === 40) {
// Reset data-clicked.
$(this).data('clicked', false);
// Bind focusout to the redirect.
$('#test').unbind('focusout').bind('focusout', function(e) {
if ($(this).val !== '') {
// An option is selected.
console.log('keyboard focusout redirect');
}
});
}
});
// Sets data-clicked on #test.
$('#test').on('click', function(e) {
// Unbind the focusout event added in the change handler.
$(this).unbind('focusout');
// Set data-clicked to be used in the change handler.
$(this).data('clicked', true);
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<select id="test" data-clicked="false">
<option value="">-- Select an Option --</option>
<option value="1">1</option>
<option value="2">2</option>
</select>
This snippet uses the HTML data attribute to set whether or not the select box was changed with a click, and sets the focusout event on the select box when the select box was changed on keypress. The redirect will occur immediately on click selection, but when using the keyboard will only occur when the select box is focused out and a value is selected.
As selection causes (in your case) navigation, the simplest solution is to avoid change event. Instead save initial value and compare against current when clicked or blured.
var defaultValue = $('#select').val();
$('#select').focus();
$('#select').on('click blur', function(event) {
if (defaultValue === $(this).val()) {
return
}
// no need to save with location.href
defaultValue = $(this).val()
console.log($(this).val());
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<select name="option" id="select">
<option value="1">1</option>
<option value="2">2</option>
</select>

jQuery collection gets broken inside each loop (remove elem, restore innerHTML)

I expected the following code to iterate through a select options and remove options having a certain value, otherwise restore the select html
var initialHTML = $('#myselect').html();
$('#myselect option').each(function(){
if($(this).val() === 'b'){
$(this).remove();
console.log('matching. removed.');
return false;
}else{
$('#myselect').html(initialHTML);
console.log('not matching. html restored.');
}
});
Expected select would be:
<select id="myselect">
<option value="a">a</option>
<option value="c">c</option>
</select>
but it actually is
<select id="myselect">
<option value="a">a</option>
<option value="b">b</option>
<option value="c">c</option>
</select>
What I'm doing wrong? FIDDLE
Update e.g. as someone suggested:
The $('#myselect option') constructs a list of options, and
$(this).remove() removes option b, but the list of options is no
longer linked to the HTML, because the HTML had been already changed
by $('#myselect').html(initialHTML) which was executed for option
a
so, I've realised that the actual problem is not even related to what I asked initially in my question. Sorry for that and I'll try to put it in the right section.
p.s. a working solution:
var initialHTML = $('#myselect').html();
function doIt(){
$('#myselect').html(initialHTML);
$('#myselect option').each(function(){
if($(this).val() === 'b'){
$(this).remove();
}
});
}
Thank you so much for help.
The main issue is this line:
$('#myselect').html(initialHTML);
It is executed for option a and then option b can no longer be removed because the HTML has changed even though it is actually identical. So remove that line and your code will work.
$('#myselect option') constructs a list of options, and $(this).remove() removes option b from the list, but the list is no longer associated with the HTML, because the HTML had been already changed by $('#myselect').html(initialHTML) which was executed for option a.
var initialHTML = $('#myselect').html();
$('#myselect option').each(function() {
if (this.value === 'b') {
$(this).remove();
console.log('matching. removed.');
return false;
} else {
//$('#myselect').html(initialHTML);
console.log('not matching. html restored.');
}
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<select id="myselect">
<option>a</option>
<option>b</option>
<option>c</option>
</select>
I changed $(this).val() to this.value. Both will work, but the second one is a bit more efficient because there is no point in converting the JavaScript object to a jQuery object just to get the value.
However, remember that the line:
return false;
will cause the each() loop to exit once a match is found. That's OK if there will only be one match. If there can be more that one match, remove that line so the loop can go through all options.
Here is an alternative way:
Other answers already point out the reason, as because you refresh the option list the during the first iteration, $(this).remove() no longer works because it lose contact of the original item. Thus, a simple fix here would be re-select the target based on it's value. And you can keep your original code of $('#myselect').html(initialHTML); in the loop.
var initialHTML = $('#myselect').html();
$('#myselect option').each(function(){
if($(this).val() === 'b'){
$("option[value='" + $(this).val() + "']").remove();
console.log('matching. removed.');
return false;
} else {
$('#myselect').html(initialHTML);
console.log('not matching. html restored.');
}
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<select id="myselect">
<option value='a'>a</option>
<option value='b'>b</option>
<option value='c'>c</option>
</select>
Try this:
you don't need else part as it is already present and put your jquery inside document.ready so that it will ensure DOM is ready and you can run the jquery script.
var initialHTML = $('#myselect').html();
$(document).ready(function(){
$('#myselect option').each(function(){
if($(this).text() == 'b'){
$(this).remove();
console.log('matching. removed.');
return false;
}
});
});
JSFiddle
What is happening here is that you have something that only needs to be false once to restore the entire select. So it does remove 'b' when it finds it, but then restores the whole select once it sees that 'c' !== 'b'.
You can achive your expected behavior by removing:
else{
$('#myselect').html(initialHTML);
console.log('not matching. html restored.');
}
Depending on what your ultimate goal is, there may be better ways of approaching this, though.
Hope that helps.

Keypress event on select2 textbox is not working

I have a select2 dropdown.
<select class="eselect2" type="text" id="qename" style="width: 390px;">
<option value="1">NY</option>
<option value="2">MA</option>
<option value="3">PA</option>
<option value="4">CA</option>
</select>
with simple javascript
$( document ).ready(function() {
console.log( "ready!" );
$(".eselect2").select2();
$('.select2-search__field').on("keydown", function(e) {
console.log(e.keyCode); // nothing happens
if (e.keyCode == 13) {
}
});
});
I have an event associated with keypress on the input field. It does not get fired since, the textbox is destroyed and recreated each time the dropdown arrow in the select2 is clicked.
I have attached a fiddle for clarity.
http://jsfiddle.net/sizekumar/ckfjzkhj/
Really jQuery keypress event doesn't fire on input.select2-search__field element. But pure js event does. This piece of code works for me
document.getElementsByClassName('select2-search__field')[0].onkeypress = function(evt) { console.log(evt); }
EDIT: This doesn't solve the problem.
I wouldn't call this a duplicate, but I think this answer directly applies: https://stackoverflow.com/a/30697173/5948237
$('.select2-input').on("keydown", function(e) {
if (e.keyCode == 13) {
}
});

How can I change selected value of a DropDownList with having change method?

I was wondering if it's possible to get jQuery to select an option with having predefined change method.
Let me give me an example; I have a drop-down list like this:
<select id="ddl_BankRequestType">
<option>-- Request Type ---</option>
<option value="val1">Item1</option>
<option value="val2">Item2</option>
</select>
If user selects Item1 or Item2 some logic will be executed:
$('#ddl_BankRequestType').change(function () {
var selectedItem = $(this).find(":selected").text();
if (selectedItem === 'Item1') {
// codes
} else if (selectedItem === 'Item2') {
//other codes
}
});
It works like a charm. Now in document.ready I want the second option be selected item, so I put this code:
$('#ddl_BankRequestType > option:eq(2)').attr('selected', true);
It also works, but I want the logic for Item2 to execute. I have tried the following, but they do nothing:
$('#ddl_BankRequestType > option:eq(2)').attr('selected', true).change();
$('#ddl_BankRequestType > option:eq(2)').attr('selected', true).trigger('change');
You can check the code from DEMO
$(function (){
$('#ddl_BankRequestType').change(function () {
var selectedItem = $(this).find(":selected").text();
console.log(selectedItem)
if (selectedItem === 'Item1') {
alert('item1');
} else if (selectedItem === 'Item2') {
alert('item2');
}
});
$('#ddl_BankRequestType > option:eq(2)').attr('selected', true).change();
});
A quick fix should be something like below,
$('#ddl_BankRequestType').on("change",function () {
var selectedItem = $(this).find(":selected").text();
if (selectedItem === 'Item1') {
doItem1();
} else if (selectedItem === 'Item2') {
doItem2();
}
});
function doItem1(){
alert('Item1');
//other codes
}
function doItem2(){
alert('Item2');
//other codes
}
$(document).ready(function(){
$('#ddl_BankRequestType > option:eq(2)').attr('selected', true);
doItem2();
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<select id="ddl_BankRequestType">
<option>-- Request Type ---</option>
<option value="val1">Item1</option>
<option value="val2">Item2</option>
</select>
The problem is .attr() returns a string. So, you can't chain it to trigger the change event. You could do the following:
var ddl_BankRequestType = $('#ddl_BankRequestType');
ddl_BankRequestType.find('option:eq(2)').attr('selected', true);
ddl_BankRequestType.trigger('change');
This is likely a timing issue - since theoretically your code should be firing that trigger event based on the way you're doing it. I think it's simply firing the event before the change function is registered. jQuery's Document load lifecycle loops through all of the defined loaders in the order they're registered, so if you're not able to change when you set the option element as "selected" to after the change event is bound, you may be able to use the native timeout function to ensure it gets fired last.
I generally avoid using timeout like this, but in your scenario it may work:
setTimeout(function() { /* Set the option element true here */ }, 1000);
Best bet is to try and set the element after the change event is bound.

Getting rid of the onclick when clicked with jQuery

ok so i have some select tags of cities
<select onchange="storeCity(this.value, false)" name="search[city]" id="search_city" class="left">
<option value="">== Select City ==</option>
<optgroup label="Florida"><option selected="selected" value="ft-myers-sarasota-fl">Ft. Myers / Sarasota </option>
<option value="jacksonville-fl">Jacksonville</option>
<option value="miami-fl">Miami / Ft. Lauderdale </option>
<option value="orlando-fl">Orlando</option>
<option value="tampa-fl">Tampa</option></optgroup></select>
Some cities are not available now so i needed a lightbox to popup when they are clicked...which i have working with this code
$('#search_city').change(function(e) {
e.preventDefault();
if ($(this).val() == 'jacksonville-fl' || $(this).val() == 'miami-fl' || $(this).val() == 'tampa-fl' || $(this).val() == 'ft-myers-sarasota-fl') {
}
The problem I have is that it goes to the link anyways and i need it to get rid of the link or the onchange on the select...something is getting the page to refresh...but i dont know what
storeCity(this.value, false) might have caused the refresh
BTW, you can merge the code like this:
$('#search_city').change(function(e) {
storeCity(this.value, false);
if (this.value.match(/(jacksonville-fl|miami-fl|tampa-fl|ft-myers-sarasota-fl)/i)) {
//do some stuff
}
e.preventDefault();
});
You could probably use jQuery's .one() function for this. E.g.
$('#search_city').one('change', function() {
/* Your code */
});
The change event will only execute once.
you have a method called
storeCity(this.value, false)
on change event , this might be refreshing the page. check that out.
Also a word of warning - I'd not use e.preventDefault() on a CHANGE event (unless you really need to), as it can have some weird behaviour in some browsers. Usually that's just for CLICK events.

Categories