Backbone view event handler for checkbox and labels - javascript

I'm trying to find out whether a checkbox ids cheked o not after a user has clicked it. The checkbox is wrapped in a label which must be clickable too. My HTML code is:
<div id="view_container">a</div>
<script type="text/template" id="view_template">
<ul>
<li>
<label>
<input type="checkbox" checked="" value="a">A option
</label>
</li>
<li>
<label>
<input type="checkbox" value="b">B option
</label>
</li>
<li>
<label>
<input type="checkbox" value="c">C option
</label>
</li>
</ul>
</script>
Ans the JavaScript:
View = Backbone.View.extend({
initialize: function() {
console.log('in the init');
this.render();
},
render: function() {
var template = _.template($("#view_template").html(), {});
this.el.html(template);
},
events: {
'click ul li label': 'clicked',
},
clicked: function(e) {
e.stopPropagation();
console.log('e.target is ' + e.target);
console.log('e.currentTarget is ' + e.currentTarget);
var isChecked = $(e.currentTarget).find('input:first').is(':checked');
var selected = $(e.currentTarget).find('input:first').val().trim();
console.log('isChecked is ' + isChecked);
},
});
var view = new View({
el: $("#view_container")
});
The jsfiddle is here.
There are a few problems. If i click the input, it works fine and the value of isChecked is true. However, if i clicked the label, the function clicked is executed twice with console.log output of:
e.target is [object HTMLLabelElement]
e.currentTarget is [object HTMLLabelElement]
isChecked is false
e.target is [object HTMLInputElement]
e.currentTarget is [object HTMLLabelElement]
isChecked is true
So isChecked is false the first time.
So, how do I know if the checkbox is being checked or not, regardless of if user clicks on label or directly on the input? And how do I stop handler from executing twice when user clicks on label?

The issue is that, by nature, the labels containing <input>s or connected to them via for attribute triggers a click event on the associated input. So there are 2 click events:
Actual click on <label>
Click triggered on <input> by the label.
So if we listen to click events on label, we will catch both of them in this case - because the click triggered on <input> will also bubble up to <label> since it's a parent.
We can solve this by listening to clicks on the checkbox itself, rather than the label.
You can do the following using jQuery is method with :checkbox and :checked selectors as shown below.
View = Backbone.View.extend({
initialize: function () {
this.render();
},
template: _.template($("#view_template").html()),
events: {
'click ul li :checkbox': 'clicked',
},
render: function () {
this.$el.append(this.template({}));
},
clicked: function (e) {
var $target = $(e.target);
var selected = $target .is(':checked');
console.log('selected: ', selected, 'value: ', $target.val());
}
});
var view = new View({
el: $("#view_container")
});
Updated fiddle
Note that it's a good practice to cache the template function rather than calling _template each time render is called.

Please see the best answer here
Why Does a Label Inside an Input Trigger a Click Event .
In short, it's a specification of label element.

Related

Event handling issue with label and input inside it [duplicate]

window.onload = function(){
var wow = document.getElementById("wow");
wow.onclick = function(){
alert("hi");
}
}
<label id="wow"><input type="checkbox" name="checkbox" value="value">Text</label>
This is my code, when I clicked on "Text" it will alert hi twice but when I clicked on the box, the onclick element will only trigger once, why?
When you click on the label, it triggers the click handler, and you get an alert.
But clicking on a label also automatically sends a click event to the associated input element, so this is treated as a click on the checkbox. Then event bubbling causes that click event to be triggered on the containing element, which is the label, so your handler is run again.
If you change your HTML to this, you won't get the double alert:
<input id="wowcb" type="checkbox" name="checkbox" value="value">
<label id="wow" for="wowcb">Text</label>
The label is now associated with the checkbox using the for attribute instead of wrapping around it.
DEMO
If your intention is to respond only to clicks on the label and not on the checkbox, you can look at the event.target property. It references the element that called the listener so that if the click wasn't on that element, don't to the action:
window.onload = function(){
var el = document.getElementById('wow');
el.addEventListener('click', function(event) {
if (this === event.target) {
/* click was on label */
alert('click was on label');
} else {
/* click was on checkbox */
event.stopPropagation();
return false;
}
}, false);
}
If, on the other hand, you want to only respond to clicks on the checkbox (where a click on the label also produces a click on the checkbox), then do the reverse. Do nothing for clicks on the label and let ones from the checkbox through:
window.onload = function(){
var el = document.getElementById('foolabel');
el.addEventListener('click', function(event) {
if (this === event.target) {
/* click was on label */
event.stopPropagation();
return false;
} else {
/*
** click is from checkbox, initiated by click on label
** or checkbox
*/
alert('click from checkbox');
}
}, false);
}
This version seems to have the most natural behaviour. However, changing the markup so that the label no longer wraps the checkbox will mean the listener is not called.
Event bubble.
The checkbox is the child node of the label. You click the checkbox. Event bubble to the label. Then alert pop up twice.
To prevent alert pop up twice when you click the checkbox. You can change you onclick function into this:
wow.onclick = function(e){
alert('hi');
stopBubble(e);
}
function stopBubble(e)
{
if (e && e.stopPropagation)
e.stopPropagation()
else
window.event.cancelBubble=true
}
Hope this can work for you.
The Label tag will be associated with the input tag inside it. So when you click the label, it will also trigger a click event for the input, then bubble to the label itself.
See this:
document.getElementById("winput").addEventListener('click', function(event){
alert('input click');
//stop bubble
event.stopPropagation();
}, false);
http://jsfiddle.net/96vPP/
Rammed headfirst into this gotcha again and decided to prove what it did to myself, hopefully helping me remember. To help future people to whom the above might not be clear enough, here the example I made.
https://jsfiddle.net/ashes/0bcauenm/
$(".someBigContainer").on("click", "input[type=checkbox]", ()=> {
$output.val("Clicked: checkbox\n" + $output.val());
});
edit: added the link with a snipped of the code.
This is probably the simplest answer. Just add a span around your text and stop event propagation.
window.onload = function(){
var wow = document.getElementById("wow");
wow.onclick = function(){
alert("hi");
}
}
<label id="wow">
<input type="checkbox" name="checkbox" value="value">
<span onclick="event.stopPropagation()">Text</span>
</label>
Or without inline-javaScript
window.onload = function(){
var wow = document.getElementById("wow");
wow.onclick = function(){
alert("hi");
}
var span = document.getElementsByTagName("span")[0];
span.onclick = function(event){
event.stopPropagation();
}
}
<label id="wow">
<input type="checkbox" name="checkbox" value="value">
<span>Text</span>
</label>
For those using React and Material UI - I've encountered this issue on one of my forms. event.preventDefault() is stopping the event from bubbling as described by the accepted answer.
<Button
onClick={(event) => {
event.preventDefault()
this.handleCardClick(planName)
}}
>
<FormControl component="fieldset">
<RadioGroup
row
value={selectedPlan}
>
<FormControlLabel
control={<Radio color="secondary" />}
label={label}
/>
</RadioGroup>
</FormControl>
</Button>
Use change to listen for events instead of click

Pass Ctrl + Click Event to another element in Firefox

It's come to my understanding that Firefox will not pass Ctrl + Click from label, to target input field. I'm at a loss as to how I can achieve this behaviour. I have checkbox fields, which are hidden, and their respective labels that are styled uniquely and visible... When clicking the label, click registers, but control click does not.
I assume html as
<form>
<label for="home" id="lblhome">home</label>
<input type="checkbox" name="home" id="home"><br>
</form>
I want one thing if ctrl+cliked other thing if it's just click , the checkbox click code
$( "#home" ).click( function( e) {
if(e.ctrlKey)
alert('i just got ctrl+cliked');
else
alert('i just got clicked');
});
So now create handler for label check if it's ctrl+click if yes then create an jquery Event and change its ctrlKey property to true and trigger it on the checkbox.
$('#lblhome').click(
function(e){
if(e.ctrlKey) {
alert('ctrl+click on label');
//cancel the normal click
e.preventDefault();
e.stopPropagation();
e2 = jQuery.Event("click");
//e2.which = 50; // for other key combos like CTRL+A+click
e2.ctrlKey = true;
$( "#home").triggerHandler(e2);
$( "#home").prop("checked", !$( "#home").prop("checked"));//toggle checkbox state
}
});
DEMO : https://jsfiddle.net/35g41aaL/
This is kind of hacky, but it works: http://jsbin.com/dogelehena/edit?html,console,output
<input type="checkbox" id="cb">
<label for="cb">Label</label>
<script>
$(function () {
var cb = $('#cb');
var isFirefox = navigator.userAgent.toLowerCase().indexOf('firefox') > -1;
$('label').click(function (e) {
if (e.ctrlKey && isFirefox) {
var checked = cb.prop('checked');
cb.prop('checked', !checked);
}
});
});
</script>
First, attaching the click event to the label and not the input. This way, the event is not blocked when ctrl is held down, but the checkbox state does not change. So in the callback, we check for e.ctrlKey, and if it's true, we change that checkbox's state manually.
Please note that I added the isFirefox check, because while this functionality fixes the behavior in Firefox, it breaks in other browsers.

backbone.js events not bind to fire action on text area

I have a text area in my template .i want to pass text value from text area to function (view ) change event. I bound the "change" event to textarea but action is not working.
template
<div id="replyt" class="commentArea"> <textarea id="rep" class="form-control" placeholder="What's on your mind ?" rows="2"></textarea>
</div>
My view
var PostwallView = Backbone.View.extend({
el: $("#content"),
events: {
'change #rep': 'test',// or which event i need
},
My action
test:function(e)
{
var val = $(e.currentTarget).val();
alert( val );
e.preventDefault();
},
Here I used keyup and keydown. My event is working but action fire in first character when I am typing in text area
The input and keydown/up events are triggered when the value changes or a key is pressed. I don't know when you expect change to trigger, but blur triggers when the textarea loses focus:
'blur #rep': 'test'
"blur #rep":"yourMethod"
yourMethod:function(){
if($('#textArea').val() != null || '#textArea').val() != undefined){
/*Call your other function here*/
}
}

Backbone event adding extra events to radio button

I have a set of radio buttons that I am testing with, will be used to change theme. Html code is straight forward
<div id="options">
<input type="radio" name="imm" id="themea" value="a" /> Theme 1
<input type="radio" name="imm" id="themeb" value="b" /> Theme 2
</div>
How when I click on a radio item, the first time around the following code will show alert once, second time it will display twice, third time third and so on. What is going on, I reckon I need to destroy something but not sure what.
Here is my backbone code:
events: {
"click input[type=radio]": "onRadioClick"
},
onRadioClick: function (e) {
// e.stopPropagation();
var self = this
_.bindAll(self)
$("input:radio[name=imm]").click(function (){
var somval = $(this).val();
alert(somval)
});
This is how I initialize the view:
initialize: function(state) {
var self = this
_.bindAll(self)
self.state = state
self.setElement('#options')
self.elem = {
imm: self.$el.find('#imm')
}
}

Backbone - Checkbox stays checked?

I'm trying to hook up a checkbox to my View, but as soon as I tick it, it stays checked, even when I click it again?
Here's part of the View:
views.PaginatedView = Backbone.View.extend({
events: {
'click input.completedEnquiries': 'filterCompletedEnquiries'
},
filterCompletedEnquiries: function (e) {
return e.currentTarget.checked;
}
});
Heres the template:
<label>Show Completed: <input type="checkbox" class="completedEnquiries" /></label>
I've no idea what I'm doing wrong here?
Edit
Here is a Jsfiddle of the problem: http://jsfiddle.net/9cvVv/167/
The problem is returning e.currentTarget.checked from your event handler. Returning true or false from this handler will check or uncheck the box for you
filterCompletedEnquiries: function(e) {
//return e.currentTarget.checked;
},
comment out that return statement, and it works fine. You can still grab the info, but don't return anything from the method.
filterCompletedEnquiries: function(e) {
var isChecked = e.currentTarget.checked;
// do stuff here, based on it being checked or not
},
Edit
Here's an example, based on the conversation in the comments:
views.PaginatedView = Backbone.View.extend({
events: {
'click input.completedEnquiries': 'completedEnquiriesClicked'
},
// this is explicitly an event handler, and that's all it should be used for
completedEnquiriesClicked: function(e){
this.showCompletedEnquiries = e.currentTarget.checked;
},
doSomething Else: function (e) {
// now that we need to know, we can just check that attribute
if (this.showCompletedEnquiries){
// do something here
}
}
});
This is just one of many options you have, for making this work the way you want.

Categories

Resources