jQuery checkbox (under a ul > li) check event - javascript

I have this code to handle checkbox check event:
<html>
<head>
<style>
#leftPane {
width: 200px;
height: 500px;
border: 1px solid;
float: left;
}
#rightPane {
height: 500px;
width: 600px;
border: 1px solid;
border-left: 0px;
float: left;
}
#addTo {
border: 1px solid;
}
#addTo input {
border: none;
}
#showList ul{
list-style: none;
}
</style>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.0/jquery.min.js"></script>
<script>
$(document).ready(function(){
$('#todoItem').keypress(function(e){
if (e.which ==13) {
var item = $('#todoItem').val();
var chkbox = "<input type='checkbox' name='"+item+"'>"
$("#showList ul").append("<li>"+chkbox+""+item+"</li>");
}
})
$("#showList li ul input[type=checkbox]").change(function(){
var $this = $(this);
alert("tell me");
/*if ($this.is(':checked')) {
$(this).parent().remove();
}*/
})
})
</script>
</head>
<body>
<div id="leftPane">
<ul>
<li>Shopping List</li>
<li>Movie List`</li>
<ul>
</div>
<div id="rightPane">
<p>Let's add some todo</p>
<div id="addTo">
<input id="todoItem" type="text" placeholder="Add a todo"></input>
</div>
<div id="showList">
<ul>
</ul>
</div>
</div>
</body>
When I am clicking the checkbox the event handler
( $("#showList li ul input[type=checkbox]").change(function(){ )
is not fired (no alert appears). If I select
$('#showList > ul").click (...)
Then the event handler fires but that means clicking anywhere within the ul not necessarily for a checkbox.
I was following these links to develop the above code:
jQuery checkbox checked state changed event
Use JQuery to check a checkbox in a parent list-item?
The jsfiddle page: https://jsfiddle.net/
Any help would be much appreciated.
Thanks

my solution
$(document).on('change', "#showList ul li input[type=checkbox]", function(){
var $this = $(this);
alert("tell me");
/*if ($this.is(':checked')) {
$(this).parent().remove();
}*/
})
Problems in your solution:
you have written li before ul; you are expecting li to enclose ul tag, which is not the case rather opposite
You are dynamically adding checkbox in runtime, so normal event handler wont work like .change. You need to implement event delegation technique to work in your case.
in this code $('#showList > ul").click (...) you know what is your mistake
The majority of browser events bubble, or propagate, from the deepest,
innermost element (the event target) in the document. Delegated events
have the advantage that they can process events from descendant
elements that are added to the document at a later time.
By picking an element that is guaranteed to be present at the time the delegated event handler is attached, you can use delegated events to avoid the need to frequently attach and remove event handlers. document object is guranteed to be present all the time. So, document object will delegate the change event to element matching the selector #showList ul li input[type=checkbox].

When you register the onChange event, you do it for the matching elements currently in the page. New elements will not have that event listener registered to them.
To solve the problem, you could either add the event when creating the item itself, or you could use event delegation.
Taking advantage of events bubbling up the DOM and event delegation, register the event on a static parent (i.e #showlist), like so:
$('#showlist').on('change', 'input[type=checkbox]', function(event) {
// your handler here
});
What'll happen is that when you click on the desired element, the event will bubble up until it reaches an element that has a registered handler for its type. You'll notice that event.target is the actual event target, not the element handling the event.
Aside from that, your selector should be #showlist ul li input[type='checkbox'], not #showlist li ul input[type='checkbox'].

Let's follow KISS method.
<script>
$(document).ready(function () {
$('#todoItem').change(function () {
$('<li />').append(
$('<input type="checkbox" name="' + this.value + '" id="' + this.value + '">')
.click(function () {
var $this = this;
alert('tell me ' + $this.name);
})//click
)//append
.append('<label for="' + this.value + '">' + this.value + '</label>')
.appendTo($("#showList ul"));
});
});
</script>
Is it what you want?

Related

Clone div with new calculator for new divs

I have some Jquery that I am using to clone a div, inside the div is an input that does a calculation.
When I clone the div and it creates a new div, the calculation does not work for the new divs. I understand that the calculation only works once the way I have it written. I have searched, but cannot find what I am looking for.
I also have an issue that when I add a number in the input the calculation works for the first div, but it also removes my buttons.
How can I have a new calculation for each new cloned div?
How can I stop the calculation from removing my add/remove buttons?
function clone() {
$(this).parents(".clonedInput").clone()
.appendTo("body")
.attr("id", "clonedInput" + cloneIndex)
.find("*")
.each(function() {
var id = this.id || "";
var match = id.match(regex) || [];
if (match.length == 3) {
this.id = match[1] + (cloneIndex);
}
})
.on('click', 'button.clone', clone)
.on('click', 'button.remove', remove);
cloneIndex++;
}
function remove() {
$(this).parents(".clonedInput").remove();
}
$("button.clone").on("click", clone);
$("button.remove").on("click", remove);
// calculator
$(document).ready(function() {
$(".calculate").bind("keyup change", function(e) {
var cabwidth = parseFloat($("#cabwidth").val()) || 0;
var ply = 1.4375;
var value = cabwidth - ply;
if (!isNaN(value) && value !== Infinity) {
$("#sum").text(value);
}
});
});
body {
padding: 10px;
}
.clonedInput {
padding: 10px;
border-radius: 5px;
background-color: #def;
margin-bottom: 10px;
}
.clonedInput div {
margin: 5px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="clonedInput1" class="clonedInput">
<input type="text" class="calculate" id="cabwidth" placeholder="Cabinet Width">
<div id="sum" />
<div class="actions">
<button class="clone">Add</button>
<button class="remove">Remove</button>
</div>
</div>
Here is a jsfiddle example: jsfiddle
Your buttons get removed because their parent <div> has its contents overwritten (due to your invalid syntax). You're attempting to self-close your sum <div> with <div id="sum" />.
The <div> element cannot be self-closed, as it is not a void element; you must explicitly state that the element is empty with <div id="sum"></div>. Making this change fixes the problem with your buttons disappearing.
Note that you can validate your HTML markup with the W3C validation service, to ensure that your HTML is valid (and thus behaves in a way that is expected). Also note that .bind() is deprecated as of jQuery 3.0; you should be using .on() instead.
As for your cloning not working, that is due to two reasons:
The first being that you are cloning based on ID, and thus duplicating the ID. IDs must be unique throughout the DOM. Use classes instead of IDs, and use $(this) to refer to the specific cloned element.
Change #sum to .sum, and instead of $("#sum").text(value), use
$(this).parent().find(".sum").text(value) to only affect the
correct element.
Change var cabwidth =
parseFloat($("#cabwidth").val()) || 0 to var cabwidth =
parseFloat($(this).val()) || 0.
Remove all use of IDs to ensure valid markup after the cloning.
The second being that event handlers do not get attached to cloned elements. You need to hoist the scope to an element that is available on DOM load, and make use of event delegation. Instead of $(".calculate").bind("keyup change", function(e), use $("body").on("keyup change", ".calculate", function(e).
This is all fixed in the following example:
function clone() {
$(this).parents(".clonedInput").clone()
.appendTo("body")
.find("*")
.on('click', 'button.clone', clone)
.on('click', 'button.remove', remove);
}
function remove() {
$(this).parents(".clonedInput").remove();
}
$("button.clone").on("click", clone);
$("button.remove").on("click", remove);
// calculator
$(document).ready(function() {
$("body").on("keyup change", ".calculate", function(e) {
var cabwidth = parseFloat($(this).val()) || 0;
var ply = 1.4375;
var value = cabwidth - ply;
if (!isNaN(value) && value !== Infinity) {
$(this).parent().find(".sum").text(value);
}
});
});
body {
padding: 10px;
}
.clonedInput {
padding: 10px;
border-radius: 5px;
background-color: #def;
margin-bottom: 10px;
}
.clonedInput div {
margin: 5px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="clonedInput">
<input type="text" class="calculate" placeholder="Cabinet Width">
<div class="sum"></div>
<div class="actions">
<button class="clone">Add</button>
<button class="remove">Remove</button>
</div>
</div>
Hope this helps! :)
Here is an updated jsFiddle.
Some notes on what I changed:
.bind() is depreciated
Attached the change/keyup to the document, then passed .calculate as the selector, this will work with dynamic elements, whereas before it wasn't
Made sure each clone and child elements have a unique ID
Updated the calculate function to target elements relative to current input

:not selector with <a> in <div>

I have an a in a div and want to change the window location on click of div.
<div class="div-class">
</div>
$(document).on("click", ".div-class:not(.a-class, .a-class-2)", function() {
window.location = "/somewhere-else";
}
When clicking on either a, a new tab opens and the current window changes location. I want it to be that if you click on any a it will open a new tab, if you click on the containing div it will change window location.
To achieve this you can hook to the a elements directly and call stopPropagation() on the event passed to the handler. This will stop the event bubbling to the div and will ensure only the new tab is opened.
Similarly, you can hook to the click event of the div element to call window.location.assign() to change the page URL. Try this:
$(document).on("click", ".a-class, .a-class-2", function(e) {
e.stopPropagation();
console.log('a clicked');
}).on('click', '.div-class', function() {
console.log('div clicked');
// location.assign("/somewhere-else"); // commented out to stop breaking the snippet
});
/* this is only to make the hit areas more obvious in the snippet */
a { border: 1px solid #C00; }
div { border: 1px solid #0C0; }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="div-class">
a-class
a-class-2
</div>
Rory's answer works, but I don't think it needs two handlers or to call stopPropagation (which can be harmful). You can filter on the event target using jQuery.is
$(document).on("click", ".div-class", function(event) {
if (!$(event.target).is(".a-class, .a-class-2")) {
console.log("going /somewhere-else");
}
// You could also do
if( $(event.target).is(".div-class") ) {
console.log("going /somewhere-else v2");
}
});
a { background-color: #eee; }
div { border: 1px solid #0C0; }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="div-class">
link 1
link 2
</div>

JQuery - Listen for a "scroll" event in an dynamically added/appended element

I want to detect scrolling with JQuery in an dynamically added element. Whereas the .on() method detects events like click, scroll events doesn't seem to be recognized.
Example:
https://jsfiddle.net/cwv9h0ur/
HTML:
<input type="button" id="add" value="add Divs"/>
<div id="container"></div>
Click: <div id="out"></div><br>
Scroll: <div id="out2"></div>
Javascript:
$("#add").on("click",function(){
var div = document.createElement("div");
div.className="childDiv";
div.innerHTML=" TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST ";
$('#container').append(div);
})
$("#container").on("click",".childDiv",function (){
$("#out").html("click detected");
setTimeout(function(){$("#out").html("")},300)
});
$("#container").on("scroll",".childDiv",function (){
$("#out2").html("scroll detected");
setTimeout(function(){$("#out2").html("")},300)
});
CSS:
.container, .childDiv {
display:inline-block;
margin: 10px;
background: lightgray;
height: 100px;
width: 80px;
overflow:auto;
}
Scroll event doesn't bubble, so you cannot delegate it. However, you could capture event on all browsers which support it:
document.getElementById('container').addEventListener(
'scroll',
function(event){
var $elm = $(event.target);
if( $elm.hasClass('childDiv')){ // or any other filtering condition
$("#out2").html("scroll detected");setTimeout(function(){$("#out2").html("")},300);
}
},
true // Capture event
);
-jsFiddle-

How to fire child div event without firing parent div event?

I am trying to fire child div event but it seems that instead of child div , it is the parent div's click event that is getting fired. I tried using stopPropagation in child event but it doesnt seem to work.
$(document).ready(function() {
var lot = '<div class="divlot">This is a lot!</div>'
var lineitem = '<div class="divlineitem">This is a lineitem!</div>'
$("#container").on("click", function() {
$("#container").append(lot);
});
$(".divlot").on("click", function(e) {
e.stopPropagation();
alert($(this).attr("class"));
$(this).append(lineitem);
});
});
#container {
background-color: grey;
}
.divlot {
background-color: red;
padding-left: 20px;
}
.divlineitem {
background-color: blue;
padding-left: 20px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<div id="container">Container</div>
Event handlers are bound only to the currently selected elements; they must exist on the page at the time your code makes the event binding call.
As of now, your are using direct event handler binding for divlot which doesn't exist in the page thus neither event handler work nor e.stopPropagation()
Since you are adding event dynamically you need to use Event Delegation using .on() delegated-events approach.
Bind event using
$("#container").on("click", ".divlot", function(e) {
e.stopPropagation();
alert($(this).attr("class"));
$(this).append(lineitem);
});
$(document).ready(function() {
var lot = '<div class="divlot">This is a lot!</div>'
var lineitem = '<div class="divlineitem">This is a lineitem!</div>'
$("#container").on("click", function() {
$("#container").append(lot);
});
$("#container").on("click", ".divlot", function(e) {
e.stopPropagation();
alert($(this).attr("class"));
$(this).append(lineitem);
});
});
#container {
background-color: grey;
}
.divlot {
background-color: red;
padding-left: 20px;
}
.divlineitem {
background-color: blue;
padding-left: 20px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<div id="container">Container</div>
You are creating the child divs dynamically. So you should use event delegation for properly binding the events.
$("#container").on("click",".divlot", function(e) {
e.stopPropagation();
alert($(this).attr("class"));
$(this).append(lineitem);
});
Fiddle

How should i improve my jQuery click funciton?

So i'm learning some jQuery at the moment and got somewhat stuck with this .click function. I'm trying to "turn a light on and off", so to speak.
I am able to do so, but only once. Why is that, that my code only runs for one click event per item, and how should i improve it?
Link to my JSfiddle.
HTML
<div class="lightOn"></div>
<div class="lightOff"></div>
jQuery
$('.lightOn').click(function() {
$(this).removeClass('lightOn');
$(this).addClass('lightOff');
});
$('.lightOff').click(function() {
$(this).removeClass('lightOff');
$(this).addClass('lightOn');
});
CSS
.lightOn {
height: 90px;
width:90px;
background-color:yellow;
border-radius: 100%;
float:left;
margin:10px;
}
.lightOff {
height: 90px;
width:90px;
background-color:grey;
border-radius: 100%;
float:left;
margin:10px;
}
The issue is because you are removing the class you are selecting by, so for successive clicks the element no longer exists. Instead have a common class which remains, but add one to it to light up the object. Try this:
<div class="light"></div>
<div class="light"></div>
.light.on {
background-color:yellow;
}
.light {
height: 90px;
width:90px;
background-color:grey;
border-radius: 100%;
float:left;
margin:10px;
}
$('.light').click(function() {
$(this).toggleClass('on');
});
Example fiddle
This method has the benefit of being able to handle x number of .light elements wihtout having to amend the jQuery selector you use.
The problem is that you bind the functions to elements, not to selectors. That is to say, you bind a function that removes the class lightOn to the element that had that class originally. That function only ever removes the lightOn class and adds the lightOff class, even if that has already been done once.
There are two ways to fix this. One is with on and event delegation, which allows you to do something akin to binding to a selector. It attaches the handler to a parent element, and makes use of the fact that all ancestor elements are notified of events that originated on their descendents. So the function might be bound to document.body, but only elements that originated on an element matching the .lightOn selector will trigger the handler:
$(document.body).on('click', '.lightOn', function() {
$(this).removeClass('lightOn').addClass('lightOff');
}).on('click', '.lightOff', function() {
$(this).removeClass('lightOff').addClass('lightOn');
});
http://jsfiddle.net/lonesomeday/C6f7u/5/
Better, however, is to make use of jQuery's toggleClass function, which removes classes if the element currently has them and adds them if it doesn't.
$('.lightOn,.lightOff').click(function() {
$(this).toggleClass('lightOn lightOff');
});
http://jsfiddle.net/lonesomeday/C6f7u/2/
What about
$('.lightOn, .lightOff').click(function() {
$(this).toggleClass('lightOn lightOff');
});
Demo: Fiddle
You can try using toogleClass of jquery
http://api.jquery.com/toggleClass/
It's a good practice to attach your events to the parent element. In your case this is even mandatory, because you are changing the classes, which are used during the event binding. So, your HTML:
<div class="ligths">
<div class="lightOn"></div>
<div class="lightOff"></div>
</div>
JS:
$(".ligths").on("click", "div", function(e) {
var el = $(this);
if(el.hasClass("lightOn")) {
el.removeClass("lightOn").addClass("lightOff");
} else {
el.removeClass("lightOff").addClass("lightOn");
}
});
JSFiddle: http://jsfiddle.net/C6f7u/7/

Categories

Resources