A puzzling click event : Why such behaviour? - javascript

I was recently testing the click event (finding a textbox upon click) of a <label> and found something unusual.
In the HTML markup,
If the <input> is inside the <label>, the click event on that
label is firing two times
If the <input> is outside the <label>, the click event is
functioning as expected
To better understand what I'm trying to put forward, please refer to the fiddle at JS Fiddle
I'm baffled because of this and can't figure out the reason behind it. Any takers?

If the <input> is inside the <label>, the click event on that label is firing two times
Because events 'bubble' up through the DOM, from the element recieving the initial event (the event.target, here the <input />) through ancestors. Because the listener is attached to the parent of the <input /> it first fires on the click detected on its descendant, and then again when the click bubbles to itself.
To prevent this, you can use event.stopPropagation() to prevent bubbling.
Because the event, in this case, has already bubbled to the <label> element at the point that the alert()/event-handler is fired, you'll have to explicitly call event.stopPropagation() on the <input /> itself, rather than in the event-handler attached to the <label>:
$(function() {
$('label input').click(function(event) {
event.stopPropagation();
});
$(".v1 li label").click(function() {
var ct = $(this).find("input").val();
alert(ct);
});
$(".v2 li label").click(function() {
var ct = $(this).parent().find("input").val();
alert(ct);
});
});
ul {
padding: 10px;
}
li {
list-style: none;
margin-bottom: 5px;
}
li label {
display: block;
background-color: #f0f0f0;
padding: 5px;
cursor: pointer;
border: 1px solid #ccc;
}
li input {
display: none;
}
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<h3><i>input</i> inside <i>label</i></h3>
<ul class="v1">
<li>
<label for="l1">
<input type="radio" name="a" value="1" id="l1" />First</label>
</li>
<li>
<label for="l2">
<input type="radio" name="a" value="2" id="l2" />Second</label>
</li>
<li>
<label for="l3">
<input type="radio" name="a" value="3" id="l3" />Third</label>
</li>
</ul>
<hr />
<h3><i>input</i> outside <i>label<i></h3>
<ul class="v2">
<li>
<label for="ll1">Fourth</label>
<input type="radio" name="b" value="4" id="ll1" />
</li>
<li>
<label for="ll2">Fifth</label>
<input type="radio" name="b" value="5" id="ll2" />
</li>
<li>
<label for="ll3">Sixth</label>
<input type="radio" name="b" value="6" id="ll3" />
</li>
</ul>
References:
click().
event.stopPropagation().

In your case, click event is propagating from label to input and then again to label, hence you can find two alerts.
You can use below code when we can check the target element tag name and if it is INPUT then return otherwise process further.
$(".v1 li label").click(function(event){
if(event.target.tagName=='INPUT')
return false;
var ct= $(this).find("input").val();
alert("first "+ct);
});
JSFiddle Demo

Related

On input type=radio checked, add a class to input=text

How can I link a radio button and a text input filled so when the radio is selected the text in the input text area will also change to lets say... red-bold?
I know the logic is:
When radio-A and input-text-A is checked, add CSS class to input-text-A.
When unchecked remove class. If radio-B is selected change input-text-B, and so on...
But right now the simple script targets all text inputs.
$('input[type=text]').addClass('red');
.red {
color: red;
font-weight: bold;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="form-inline">
<label class="" for="">
<input class="" type="radio" name="answer-Q1" value="option1"> A. </label>
<input type="text" name="answers" class="" placeholder="" required>
</div>
<br>
<div class="form-inline">
<label class="">
<input class="" type="radio" name="answer-Q2" value="option1"> B. </label>
<input type="text" name="answers" class="" placeholder="" required>
</div>
Give your markup, there's actually no need to add any classes or use javascript, you can do what you want with pure CSS:
input[type="radio"]:checked + input[type="text"] {
color: red;
font-weight: bold;
}
As for how to add the class with jQuery, I tend to write "robust" solutions that are maybe a bit longer, but are not as "brittle" (meaning: if markup changes a bit, the script will still work). The way I would write this - assuming no control over markup - would be using jQuery's closest and find to locate the target text inputs:
// no-conflict-save document ready shorthand
jQuery(function($) {
// bind to the "change" event of all inputs that are radio buttons
jQuery('input[type="radio"]').on('change', function() {
// find the text input
var $text_input = $(this).closest('div').find('input[type="text"]');
// if there isn't one, get out
if ( ! $text_input.length ) {
return;
}
// if the radio button is checked, add the class
if ($(this).is(':checked')) {
$text_input.addClass('red');
} else {
// otherwise, remove the class
$text_input.removeClass('red');
}
});
});
However, if I DID have control over markup, I would add a class to the radio input element, and use that to both make the script more "generically" useful, as well as narrow down the scope of which inputs were being bound (which would allow this same script to work effectively on checkboxes + text inputs as well):
// no-conflict-save document ready shorthand
jQuery(function($) {
// bind to the "change" event of any inputs with the "watch-change" class
jQuery('input.watch-change]').on('change', function() {
// find the text input. Note, this would find multiple text inputs if they existed.
var $text_input = $(this).closest('div').find('input[type="text"]');
// if there isn't a text input to work with, get out
if ( ! $text_input.length ) {
return;
}
// if the radio button is checked, add the class
if ($(this).is(':checked')) {
$text_input.addClass('red');
} else {
// otherwise, remove the class
$text_input.removeClass('red');
}
});
});
And, honestly, with a better understanding of your project scope, it might be possible to write an even more efficient, re-usable snippet of script.
Do this:
$("input[type=radio]").on("change", function(e) {
if (e.currentTarget) {
e.currentTarget.next("input[type=text").addClass("red");
}
});
Here is the working code.
$('input:radio').click(function() {
$('label:has(input:radio:checked)').addClass('rightAnswer');
$('label:has(input:radio:not(:checked))').removeClass('rightAnswer');
});
.container {margin:0 auto; margin-top:50px;}
.rightAnswer {font-weight:bold; color:#2979FF;}
.inputAnswers {width:200px;}
.block {display:block;}
input[type="radio"]:checked + input[type="text"] {
color: #2979FF;
font-weight: bold;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="container">
<label class="block" for="answer-Q1A">
<input type="radio" class="" name="answer-Q1" value="1"> A.
<input type="text" name="answers" class="inputAnswers" id="answer-Q1A" placeholder="" required></label>
<label class="block" for="answer-Q1A">
<input type="radio" class="" name="answer-Q1" value="1"> B.
<input type="text" name="answers" class="inputAnswers" id="answer-Q1A" placeholder="" required></label>
<label class="block" for="answer-Q1A">
<input type="radio" class="" name="answer-Q1" value="1"> C.
<input type="text" name="answers" class="inputAnswers" id="answer-Q2A" placeholder="" required></label>
</div>

<a> tag inside <label> tag not triggering checkbox

I have to use <a> inside a <label> tag. Because there are many css styles only apply to <a> in our current system. The <a> tag is not linking to any pages but for styling/hovering.
See code below:
<input type="checkbox" id="my-id">
<label for="my-id">Some text</label>
But when clicking on "Some text", it doesn't toggle the checkbox status.
I've tried $.preventDefault()on the <a> tag but doesn't work.
What should i do to make the <a> behaves like a label?
If you remove the href attribute from the a element, clicking on the text will toggle the checkbox.
<input type="checkbox" id="my-id">
<label for="my-id"><a>Some text</a></label>
As the HTML spec states:
The href attribute on a and area elements is not required
Do not use <a> only for styling. Use a class on the label instead.
.my-link-style {
/* To match the cursor style for an <a> */
cursor: pointer;
/* Add more styles here */
}
<input type="checkbox" id="my-id" />
<label for="my-id" class="my-link-style">Some text</label>
Updated: this answer were given before the edit where made, which now invalidated it, still, I'll leave it for the ones it can be useful
Give the a anchor pointer-events: none
label[for="my-id"] {
cursor: pointer;
}
label[for="my-id"] a {
pointer-events: none;
}
<input type="checkbox" id="my-id">
<label for="my-id">Some text</label>
If you need to support older browser, use a pseudo element to cover it
label[for="my-id"] {
cursor: pointer;
position: relative;
}
label[for="my-id"]::after {
content: ' ';
position: absolute;
left: 0; top: 0;
right: 0; bottom: 0;
}
<input type="checkbox" id="my-id">
<label for="my-id">Some text</label>
Just put the anchor element on the outside of the label element.
<input type="checkbox" id="my-id">
<label for="my-id">Some text</label>
<input type="checkbox" id="my-id">
<label for="my-id">Some text</label>
<script>
document.querySelector("label[for=my-id] > a")
.onclick = function(e) {
var el = document.getElementById(
this.parentElement.htmlFor
);
el.checked = !el.checked;
}
</script>
You need to reverse, include label in a:
<input type="checkbox" id="my-id" >
<label for="my-id">Some text</label>
JSFiddle

input type radio button checked else statement not working in jquery

I want to add a class in the parent wrapper of radio button, I did so , it is working but else statement is not workin
Here is my code
<div class="radio">
<input type="radio" name="name[]" checked>
</div>
<div class="radio">
<input type="radio" name="name[]">
</div>
This is my JS
$(document).ready(function(){
$('input[type="radio"]').on('click',function(){
if($(this).prop('checked')){
$(this).parent().addClass('new')
}
else {
$(this).parent().removeClass('new')
}
});
})
I want to remove the class new when the radio button is not checked, but it is not working.
e.g.
http://codepen.io/amitabha197/pen/KzqvWv
After webeno and mhodges pointed me to the right direction here is my solution.
$('input[type="radio"]').on('change',function(){
$(this).parent().addClass('new').siblings().removeClass('new');
});
Also point to remember, If at all you had been trying to uncheck a single radio button then know this.
You cannot uncheck a radio button. All the clicks you do on it will keep it checked, Use checkbox instead.
Why is it impossible to deselect HTML “radio” inputs?
Unable to uncheck radio button
use this function will solve you problem
$(document).ready(function(){
$('input[type="radio"]').on('change',function(){
$(this).parent().addClass('new');
$('input[type="radio"]').not(this).parent().removeClass('new');
});
});
Edited
For code optimization & modularity.
$(document).ready(function(){
$('input[type="radio"]').on('click',function(){
var name = $(this).attr("name");
$('input[type="radio"][name="'+name+'"]').closest(".radio").removeClass("new");
$(this).closest(".radio").addClass("new");
});
});
You can include <label> element as parent of <input type="radio"> elements at html.
The HTML Label Element (<label>) represents a caption for an item
in a user interface. It can be associated with a control either by
placing the control element inside the <label> element, or by using
the for attribute. Such a control is called the labeled control of
the label element. One input can be associated with multiple labels.
The parent label element will be associated with :checked first child descendant input.
You can style css :after pseudo element compounded to :checked selector to display blue border when input element is :checked.
div.radio {
display: inline-block;
}
label.radio {
display: inline-block;
width: 100px;
border: 1px solid #000;
}
label.radio > :checked:after {
content: "";
position: relative;
display: inline-block;
width: 100px;
height: 20px;
left: -6px;
top: -4px;
border: 1px solid blue;
}
<div class="radio">
<label class="radio">
<input type="radio" id="radio1" name="name[]" checked>
</label>
</div>
<div class="radio">
<label class="radio">
<input type="radio" for="radio2" name="name[]">
</label>
</div>
If requirement is to use existing html, jQuery, you can utilize .click(), .toggleCLass() to add, remove "new" className if descendant input element checked property is false at click event, set input checked property to true
$(document).ready(function() {
var radio = $(".radio");
radio.click(function(e) {
var input = e.target.querySelector("input[type=radio]");
if (!input.checked) {
radio.toggleClass("new");
input.checked = true;
}
})
})
.radio {
display: inline-block;
width: 100px;
border: 1px solid #000;
}
.new {
display: inline-block;
width: 100px;
border: 1px solid blue !important;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js">
</script>
<div class="radio new">
<input type="radio" name="name[]" checked>
</div>
<div class="radio">
<input type="radio" name="name[]">
</div>

clicking event not working on clicking inner span element

I have dom constructed like this . there are added dynamically depending on the count from the database.
<div class="todo-task">
<input type="checkbox" id='check1' />
<label for='check1'> asdasdasdasdasd
<span class="todo-remove mdi-action-delete"></span>
</label>
<div>
<input/>
<label>
<span></span>
</label>
</div>
This is my DOM structure
When I click on the inner span element click event not working. When I keep the span out side of the div as an independent element it works fine. Any suggestions why click is not raising in the inner span element
This is my click event
$(document).on('click', '.todo-remove', function () {
alert( 'Delete');
});
The 'span' element doesn't have a .todo-remove class, in this case the event wont be called. Try to add the class to span and add some style like this:
padding: 20px;
display: block;
float: right;
Of course, you'll change this code later.
Just try this javascript. It will work for you.
$("label[for='check1']").click(function(){
alert("deleted");
});
The following code woks for me:
http://jsfiddle.net/Lknca8f1/
<div class="todo-task">
<input type="checkbox" id='check1' />
<label for='check1'> asdasdasdasdasd
<span class="todo-remove mdi-action-delete"></span>
</label>
</div>
.todo-remove{
padding:20px;
background: #f00;
cursor:pointer;
}
$(function(){
$(document).on('click','.todo-remove', function(){
$(this).closest('.todo-task').remove();
});
})
Working code link Codepen
My be you are registered event before DOM ready.
Please register after ready event.
<div class="todo-task">
<input type="checkbox" id='check1' />
<label for='check1'> Click
<span class="todo-remove mdi-action-delete">Span</span>
</label>
</div>
$(document).ready(function(){
$(document).on('click', '.todo-remove', function () {
alert( 'Delete');
});
});
A workaround is to set the z-buffer of the inner span to -1
i.e.
<div class="todo-task">
<input type="checkbox" id='check1' />
<label for='check1'> asdasdasdasdasd
<span class="todo-remove mdi-action-delete" style="z-index:-1"></span>
</label>
</div>

Why does listener activate twice?

I have the following html:
<ul id="all-terminals">
<li data-terminal-id="101" class="">
<label>
<input type="radio" name="terminal" class="all" data-terminal-id="101">
<a ...></a>
...
</label>
</li>
<li data-terminal-id="100" class="active">
<label>
<input type="radio" name="terminal" class="all" data-terminal-id="100">
<a ..></a>
...
</label>
</li>
<li data-terminal-id="102" class="">
<label>
<input type="radio" name="terminal" class="all" data-terminal-id="102">
<a...></a>
...
</label>
</li>
</ul>
I wrote the following listener:
$(document).on('click', '#all-terminals li label', function(){alert(123)});
when I click on label, I see that alert executes twise.
Why?
Clicking a label automatically creates a click event on an input within it.
Assuming all your labels have inputs, you can simply change to:
$(document).on('click', '#all-terminals li input', function(event) {
alert(123);
});
Fiddle
Not exactly sure why, but mouseup works.
$(document).on('mouseup', '#all-terminals li label', function(){alert(123)});
I think bubbling is occurring in your case .
Read about on Bubbling and Capturing
I'm not completely sure, but I think that when you click on the label, you hit 2 of its child overlapping elements. Because they are part of the label but still separate elements, it fires two click events on label.
On the fiddle, it appears the it's the [a] element overlapping the ...

Categories