Javascript double alerts after onclick - javascript

I have three checkboxes in array. I handle every click of user and add value of checkbox into array. For example, page have been loaded, user have been clicked on a1, then values contains a1. If user clicks a1 and a3, values contains a1,a3 etc...
If I click into checkbox square, it works ok, but if I click on text of checkbox, js alerts two versions of array - previous(I don't need this!) and actual. Can anyone help?
Fiddle: https://jsfiddle.net/94bdepx0/
Markup
<label class="checkbox-button"><input autocomplete="off" name="tag[]" value="a1" type="checkbox">a1 text</label>
<label class="checkbox-button"><input autocomplete="off" name="tag[]" value="a2" type="checkbox">a2 text</label>
<label class="checkbox-button"><input autocomplete="off" name="tag[]" value="a3" type="checkbox">a3 text</label>
JS
$(".checkbox-button").bind("click", function (event) {
var values = $("input[name='tag[]']:checked").map(function () {
return this.value;
}).get();
alert(values);
});

Here you are:
$(".checkbox-button input").bind("click", function(event) {
var values = $("input[name='tag[]']:checked").map(function() {
return this.value;
}).get();
alert(values)
})
Your problem is that you select wrong element. You should select input instead of the label
The updated version is here: https://jsfiddle.net/94bdepx0/1/

Your click event is bound to the label. If you bind it to the checkbox, the double alert goes away.
$("input[type=checkbox]").bind("click", function(event) {
var values = $("input[name='tag[]']:checked").map(function () {
return this.value; }).get();
alert(values)
})

Instead of listening for a 'click' on the parent element, listen for 'change' on the input itself.
$(".checkbox-button").bind("click", function(event) {
...
});
becomes:
$(".checkbox-button input").bind("change", function(event) {
...
});

Related

How to corectly bind events in a loop

On a page I have couple of divs, that look like this:
<div class="product-input">
<input type="hidden" class="hidden-input">
<input type="text">
<button class="remove">X</button>
</div>
I'm trying to bind an event to that remove button with this code (simplified):
$('.product-input').each(function() {
product = $(this);
product_field = product.find('.hidden-input');
product.on('click', '.remove', function(event) {
product_field.val(null);
});
});
It works perfectly when there is only one "product-input" div. When there is more of them, all remove buttons remove value from the hidden field from the last product-input div.
https://jsfiddle.net/ryzr40yh/
Can somebody help me finding the bug?
You dont need to iterate over the element for binding the same event. you can rather bind the event to all at once:
$('.product-input').on('click', '.remove', function(event) {
$(this).prevAll('.hidden-input').val("");
});
If the remove buttons are not added dynamically, you will not need event delegation:
$('.remove').click(function(event) {
$(this).prevAll('.hidden-input').val("");
});
Working Demo
You need to declare product and product_field as local variables, now they are global variables. So whichever button is clicked inside the click handler product_field will refer to the last input element.
$('.product-input').each(function() {
var product = $(this);
var product_field = product.find('.hidden-input');
product.on('click', '.remove', function(event) {
product_field.val(null);
});
});
Demo: Fiddle
But you can simplify it without using a loop as below using the siblings relationship between the clicked button and the input field
$('.product-input .remove').click(function () {
$(this).siblings('.hidden-input').val('')
})
Demo: Fiddle

jQuery Show / Hide Option Based on Input Value

I have a form that needs to display specific select options ("locations") based on the user's ZIP, which is in a number field above. In this example, I need the option "Out of Range" to hide and the "In Range" to show when the user enters "12345" in the input.
This is my HTML:
<!--Zip-->
<div class="zip-field">
<label for="zip">Zip Code</label>
<input type="number" id="zip" name="zip" />
</div>
<!--Location-->
<div class="location-field">
<label for="location">Location</label>
<select id="location" name="location">
<option value="In Range">In Range</option>
<option value="Out of Range">Out of Range</option>
</select>
</div>
This is my jQuery:
$('#zip').on('change',function(){
if ( $(this).val() == "12345" ) {
$("#location option[value='In Range']").show();
$("#location option[value='Out of Range']").hide();
}
});
Should be pretty straightforward, but no cigar.
There are two things you need to change here:
You need to set an event handler to the zip input element. $('#zip').val( ... ); only sets the value once when that line is executed.
You need to select the option better. $("#location option[value='In Range']").show(); will not show the option you want. You have to set the value of the selector input to a value that matches the option you want.
Change your javascript to this:
$("#zip").on('blur', function(){
if ( $(this).val() == "12345" ) {
$("#location").val("In Range");
}else{
$("#location").val("Out of Range");
}
});
Notice that I'm using $('#zip').on('blur', ...); to register an event handler, setting it to the blur event and passing in a function to be executed when that event fires.
Then I set the value of the location selector input to the correct value of the option you want to select.
DEMO
Hiding an option won't work across browsers, it is same as binding event to option elements you can do only very limited thing with them. instead remove them and cache them for later use.
$(function(){
var $location = $('#location');
$location.data('options', $location.find('option')); //cache the options first up when DOM loads
$('#zip').on('change', function () { //on change event
$location.html($location.data('options')); //populate the options
if ($(this).val() == "12345") { //check for condition
$location.find("option[value='Out of Range']").remove(); //remove unwanted option
}
});
});
Fiddle
You should monitor the the value as it changes using the method below:
$('#zip').on('keyup',function(){
$("#location").val('Out Of Range');
if ( $(this).val() == "12345" ) {
$("#location").val('In Range');
}
});
The on function binds an event listen to that Element. The keyup event listens for when the key is released inside the your field. You can then compare the value to what ever and show / hide as needed.

JQuery focus to previous clicked input bug

I have two input fields. The main idea is that whenever you focus to one of the fields, the button click should print a random number inside it.
My problem is that when you just focus on (click on) the first field, then focus on second (or vice versa), the button click prints to both instead of just to the (last) focused field.
You can try to recreate the problem here: http://jsfiddle.net/sQd8t/3/
JS:
$('.family').focus(function(){
var iD = $(this).attr('id');
$("#sets").one('click',function() {
var ra = Math.floor((Math.random()*10)+1);
$('#'+iD).val(ra);
});
});
HTML:
<center class='mid'>
<input type="text" class="family" id="father" />
<br/><br>
<input type="text" class="family" id="mother" />
<br/>
<br/>
<input type='submit' value='Set text' id="sets"/>
</center>
In the "focus" handler, unbind any existing "click" handler:
$('#sets').unbind('click').one('click', function() { ... });
The way you had it, an additional one-shot "click" handler is bound each time a field gets focus, because jQuery lets you bind as many handlers as you like to an event. In other words, calling .one() does not unbind other handlers. When the click actually happens, all handlers are run.
edit — sorry - "unbind" is the old API; now .off() is preferred.
Put the variable iD outside, and separate the functions:
http://jsfiddle.net/sQd8t/8/
This prevents from adding too many events on each input click/focus.
No need to unbind.
var iD;
$('.family').focus(function() {
iD = $(this).attr('id');
});
$("#sets").on('click', function() {
var ra = Math.floor((Math.random() * 10) + 1);
if (iD!="")$('#' + iD).val(ra);
iD = "";
});
See http://jsfiddle.net/sQd8t/11/
$('.family').focus(function(){
$("#sets").attr('data-target',$(this).attr('id'))
});
$("#sets").click(function() {
var target=$(this).attr('data-target');
if(target){
var ra = Math.floor((Math.random()*10)+1);
$('#'+target).val(ra);
}
});
You can create a data-target attribute which contains the field which must be modified.

jquery each for all text boxes

I am using following script to bind a keypress event on each textbox so that on reaching the maxlength, focus will switch to next input field. Passing classname as the prarameters to the function.
function autoFocusPhoneFields(txtbox1ID,txtbox2ID) {
$('input.'+txtbox1ID+', input.'+txtbox2ID+'').each(function() {
$(this).bind('keypress', function(){
if(this.value.length == $(this).attr('maxlength')) {
$(this).next('input').focus();
}
});
});
}
$(document).ready(function(){
autoFocusPhoneFields('mobileprefix','mobilecode');
});
As i have mentioned two different input ..it is runnign fine. Butis there any way around so that it will get the classnames and runs through each input box to attach keypress event.
If I understand you correctly, you want to attach the same event handler to every input field? Just use the selector:
$(':text')
(for all input type="text") fields.
So just change
$('input.'+txtbox1ID+', input.'+txtbox2ID+'').each(function() {
to:
$(':text').each(function() {
If I get you correctly you just need to use type selector for input. You can also get rid of calling each to iterate thru inputs since binding event to multiply elements interates via them. So you can change your code into something like following:
var autoFocusPhoneFields = function () {
$('input:text').keypress(function() {
if(this.value.length == $(this).attr('maxlength'))
$(this).next('input').focus();
});
}
$(autoFocusPhoneFields);
This works fine.
HTML
<input id="one" class="inp" maxlength="5" />
<input id="two" class="inp" maxlength="3" />
<input id="three" class="inp" maxlength="2" />
JS Part
$(function(){
var onpress = function(){
var val = $(this).val();
var next_input = $(this).next('input');
var mx = $(this).attr('maxlength');
try {
mx = Number(mx);
if (next_input.length >= 1 && val.length >= mx){
next_input.focus();
}
} catch(x){}
}
$('input.inp').bind('keypress', onpress);
});

check/uncheck radio input with javascript

I have a radio input group. If a radio is checked and I click again it becomes unchecked.
Is there a way to get the previous status of the radio onClick event?
<input name="options" type="radio" onClick="resetMeIfChecked()">
<input name="options" type="radio" onClick="resetMeIfChecked()">
<input name="options" type="radio" onClick="resetMeIfChecked()">
jQuery edition
// bind to retrieve old status
$('input[type="radio"]').mousedown(function() {
// if it was checked before
if(this.checked) {
// bind event to reset state after click is completed
$(this).mouseup(function() {
// bind param, because "this" will point somewhere else in setTimeout
var radio = this;
// apparently if you do it immediatelly, it will be overriden, hence wait a tiny bit
setTimeout(function() {
radio.checked = false;
}, 5);
// don't handle mouseup anymore unless bound again
$(this).unbind('mouseup');
});
}
});
But again, this is not how radio buttons are intended to be used. I think you'd be better of with a set checkbox'es where you could uncheck all other checkboxes than the current clicked (hence always max 1 selected)
A working example
I use this. You simply store the pre-click value and ! it into the value.
<input type=radio name="myoptions" value="1"
onmousedown="this.tag = this.checked;" onclick="this.checked = !this.tag;">
This behavior is not the expected one for radio buttons and I don't recommend it at all. Try to find another way of achieving this. Use another widget or another option to reset the field value:
http://jsfiddle.net/marcosfromero/rRTE8/
try this:
function resetMeIfChecked(radio){
if(radio.checked && radio.value == window.lastrv){
$(radio).removeAttr('checked');
window.lastrv = 0;
}
else
window.lastrv = radio.value;
}
<input value="1" name="options" checked="checked" type="radio" onClick="resetMeIfChecked(this)" />A
<input value="2" name="options" type="radio" onClick="resetMeIfChecked(this)" />B
<input value="3" name="options" type="radio" onClick="resetMeIfChecked(this)" />C
Its quite simple. Just follow the simple example and
var rdblength=document.formname.elementname.length;
alert('length='+rdblength);
for(i=0;i<rdblength;i++){
document.formname.elementname[i].checked=false;
}
Just find the length and make every index checked=true/false.
Ping me at:-
http://manojbardhan2009.blogspot.com
I had the same problem and figured it out. None of the answers above work exactly as I wanted - most of them require an additional button to reset the radio. The goal was to uncheck radio by clicking on the radio itself.
Here's the fiddle: http://jsfiddle.net/MEk5Q/1/
The problem was very complicated because the radio button value changes BEFORE the click event fires so when we're listening to the event we can't tell if the radio button was already checked or not. In both cases it is already checked.
Another approach was to listen to mousedown event. Unlike click, it fires before changing radio checked attribute but unchecking it inside event handler gives us nothing since it is checked back again during mouseup event.
My answer is a little ugly workaround so I generally don't suggest it to others and I'll probably abandon it myself. It works but it involves 20ms timeout function which I'm not fond of in cases like this.
Here is the code explanation:
$('input[type="radio"]').on('mousedown', function() {
if (this.checked) { //on mousedown we can easily determine if the radio is already checked
this.dataset.check = '1'; //if so, we set a custom attribute (in DOM it would be data-check="1")
}
}).on('mouseup', function() {
if (this.dataset.check) { //then on mouseup we determine if the radio was just meant to be unchecked
var radio = this;
setTimeout(function() {radio.checked = false;}, 20); //we give it a 20ms delay because radio checking fires right after mouseup event
delete this.dataset.check; //get rid of our custom attribute
}
});
As a timeout function I could use a string (less writing) but as far as I know it would be eval'ed. Though I don't trust eval function, I prefered anonymous function.
One more thing - one could ask why spreading the code into two separate event handlers while we can fire the timeout function on mousedown? Well, what if someone press the mouse on a radio and holds it for a few secs or even someone is simply a very slow person ;). Generally, with this solution we omit the problem of lag between mousedown and mouseup.
If you need some more info about dataset, here's the MDN reference:
https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement.dataset
This property came with HTML5 and might be not cross-browser, I guess, so if you want 100% compatibility, replace it with any other solution that'll contain the data, you name it.
Sorry about jQuery here and there but I hope you're fine with it - it was much easier that way.
Hope you'll enjoy it.
$('input[type="radio"]').on("mousedown", function () {
if (this.checked) {
$(this).one("click", function () {
this.checked = false;
});
}
});
I was never too happy about being forced to aim at that tiny radio button, so I came up with a larger target AND a way to turn a radio group off without resorting to anything that would upset the HTML / JavaScript purists.
The technique relies on not molesting the radio buttons at all via event handlers, but checking for a readonly proxy for each one instead. Everything is contained in what's below in pure JavaScript using a radio group to select a type of cheese, or no cheese at all.
I purposely used no styling in this example to avoid that added layer. The dump button will tell you what the three checked states are, so use it to interrogate what happened after hitting the radio or text input elements. For example simplicity I used a global to remember the former state, but a more elegant method is to use a dataset, which I what I use in the real code of my application.
<!DOCTYPE html>
<html>
<head>
<title>Uncheck a radio button</title>
<script>
function attachEventListener(target, eventType, functionRef, capture) {
"use strict";
if (typeof target.addEventListener !== 'undefined') {
// Most modern browsers
target.addEventListener(eventType, functionRef, capture);
} else if (typeof target.attachEvent !== 'undefined') {
// IE
target.attachEvent('on' + eventType, functionRef);
} else {
eventType = 'on' + eventType;
if (typeof target[eventType] === 'function') {
var oldListener = target[eventType];
target[eventType] = function() {
oldListener();
return functionRef();
};
} else {
target[eventType] = functionRef;
}
}
}
</script>
</head>
<body>
<form>
<input id="Cheddar-radio" class="radio" type="radio" name="Cheeses-0" value="Cheddar Cheese" tabindex="-1"></input>
<input id="Cheddar-text" type="text" readonly value="Cheddar Cheese" tabindex="-1"></input><br>
<input id="Swiss-radio" class="radio" type="radio" name="Cheeses-0" value="Swiss Cheese" tabindex="-1"></input>
<input id="Swiss-text" type="text" readonly value="Swiss Cheese" tabindex="-1"></input><br>
<input id="American-radio" class="radio" type="radio" name="Cheeses-0" value="American Cheese" tabindex="-1"></input>
<input id="American-text" type="text" readonly value="American Cheese" tabindex="-1"></input><br><br>
<input onclick="dumpStates()" type="button" name="button" value="dump" tabindex="-1"></input>
</form>
<script>
window.onload = addRadioListeners;
function addRadioListeners() { // But do it on the -text elements.
attachEventListener(document.getElementById('Cheddar-text') , 'mousedown', rememberCurrentState, false);
attachEventListener(document.getElementById('Swiss-text') , 'mousedown', rememberCurrentState, false);
attachEventListener(document.getElementById('American-text'), 'mousedown', rememberCurrentState, false);
attachEventListener(document.getElementById('Cheddar-text') , 'mouseup', checkNewState, false);
attachEventListener(document.getElementById('Swiss-text') , 'mouseup', checkNewState, false);
attachEventListener(document.getElementById('American-text'), 'mouseup', checkNewState, false);
}
function dumpStates() {
console.log(document.getElementById('Cheddar-radio').checked +
' ' + document.getElementById('Swiss-radio').checked +
' ' + document.getElementById('American-radio').checked);
}
var elementWasChecked; // Global - Could just as well use a dataset attribute
// on either the -radio or -text element and check it instead.
function rememberCurrentState(event) {
var element;
var radioElement;
element = event.target;
radioElement = document.getElementById(element.id.replace(/text/,'radio'));
elementWasChecked = radioElement.checked;
radioElement.checked = true;
}
function checkNewState(event) {
var element;
var radioElement;
element = event.target;
radioElement = document.getElementById(element.id.replace(/text/,'radio'));
var currentState = radioElement.checked;
if (elementWasChecked === true && currentState === true) {
console.log('Changing ' + radioElement.id + ' to false.');
radioElement.checked = false;
}
};
</script>
</body>
</html>
If you click on the radio buttons they work as expected. If you click on the text items next to each, they are a proxy for the radio buttons with one exception. If you click on a text item that has an associated radio button that's already checked, it will uncheck it. Therefore, the text proxy's are event triggered, and not the radio buttons.
The added benefit is that you can now hit the larger text target too.
If you want to make it simple and wouldn't mind using a double-click event try something like this:
<input name="options" type="radio" ondblclick="this.checked=false;">
#jerjer's answer is almost perfect, but radios can be switched also by arrows if the radio group has the focus (so mousedown event is not enough). Alas, the radio group also gets checked when activated by focus shift (Tab), which can undesirably check one option. Therefore space should uncheck the focused radio, just like the checkbox behavior.
This code fixes that for all radios (Most credit still goes to jerjer):
window.addEventListener("DOMContentLoaded", function() {
var radios = document.querySelectorAll("input[type=radio]");
for(var i=0; i<radios.length; ++i) {
radios[i].addEventListener("click", function(e) {
if(e.target.checked && e.target.value == window.lastrv){
e.target.checked = false;
window.lastrv = 0;
}
else
window.lastrv = e.target.value;
});
radios[i].addEventListener("keypress", function(e) {
if(e.keyCode==32) e.target.click();
});
}
});

Categories