Trouble with javascript click() method / html - javascript

Thank you in advance for looking at this.
My webapp allows a user to select choices from four different drop-down menus. When the user makes a selection, the program successfully performs the following click() function which creates a new span element within a div element:
var activeFilterNames = [];
$('.filter').click(function()
{
if (!this.classList.contains('disabled'))
{
//above checks to see if link has already been clicked
//and is therefore disabled. If not, go ahead.
activeFilterNames.push(this.textContent);
//above adds name of selected link to array
i = activeFilterNames.length;
var newFilter = document.createElement('span');
newFilter.id = activeFilterNames[i-1];
newFilter.className = this.className+" activated";
//above adds a new class to identify the filter as 'activated'
//above retains existing classname to identify
//which of the four filters it came from
newFilter.innerHTML = activeFilterNames[i-1];
//above creates display text to be rendered in browser
document.getElementById('active_filters').appendChild(newFilter);
//above is the div in which span will be appended to other spans.
$("#active_filters > span").removeClass('filter');
//above removes .filter class so that this newly created span does
//not respond to the .filter click() function.
$(this).addClass('disabled');
//above adds 'disabled' class to the originally
//clicked link, causing it to skip this block of code
}
}
);
Everything appears to work fine up to this point (though I may be missing something). Essentially I am creating span elements that come out looking like this in html:
<span id="id_name" class="menu_name activated">Rendered Name</span>
And since the above span does not have the filter class, I then try to create a new function in javascript (just as a test) to make the element responsive:
$('.activated').click(function()
{
alert('hi');
}
);
But no luck. I've tried to re-render the dynamically created elements by nesting div or a inside the span, modifying the code as needed, but still nothing. I would like to keep the span because it's the only way I've found to wrap these dynamically generated elements to a second line within the div (#active_filters) where they are being created.
Can anyone see what I'm doing wrong given that I want to make the activated click() function responsive within each newly created span element?

Your binding will not work if you attempt to bind to DOM elements contained in $('.activated') before creating them. What this usually means is that you need that event listener to bind after creating. If you're dynamically creating DOM elements, you need to do something like this:
var activeFilterNames = [];
$('.filter').click(function()
{
if (!this.classList.contains('disabled'))
{
activeFilterNames.push(this.textContent);
i = activeFilterNames.length;
var newFilter = document.createElement('span');
newFilter.id = activeFilterNames[i-1];
newFilter.className = this.className+" activated";
newFilter.innerHTML = activeFilterNames[i-1];
document.getElementById('active_filters').appendChild(newFilter);
$('.activated').unbind('click');
$('.activated').click(function()
{
alert('hi');
}
);
$("#active_filters > span").removeClass('filter');
$(this).addClass('disabled');
}
}
);
Notice, before binding we unbind. This makes sure that if you do this multiple times, you aren't binding 2, 3, 4 times to the same DOM element.

You need to attach click event on dynamically created elements. In jQuery this can be done using on method if you will pass your selector as second argument and attach click to some parent element, body for example:
$( 'body' ).on( 'click', '.activated', function()
{
alert('hi');
}
);

Related

jQuery selector precedence when using pattern matching

I am creating a form that implements a bunch of similar elements. They are custom select boxes, created out of <ul>s.
Some of these elements are slightly different in the way I want the mousedown event to be handled though.
The way I have it set up currently is that, by appending _custom_select to the end of an elements class name, it will be treated as one of these special elements as far as CSS is concerned.
However, when the string selections is found inside a class name (that will coincidentally also end with _custom_select in order to apply the proper styling) I want to use a different mousedown event handler.
This is the relevant section of my event listener set up:
$('[class$="_custom_select"] li').mousedown(function(event){
var opt= event.target;
if(opt.className!='li_disabled' && event.which==1)
{
if(opt.className=='li_unselected'){
opt.className= 'li_selected';
}
else{
opt.className= 'li_unselected';
}
update_selections(opt.parentElement);
}
});
$('[class*="selections"]').mousedown(function(event){
var opt=event.target;
if(event.which==1){
if(opt.className=='li_unselected'){
opt.className= 'li_selected_2';
}
else{
opt.className= 'li_unselected';
}
}
});
This code works, but notice how, in the second binding, I had to bind the event listener to the ul that holds the li that is actually being clicked.(The ul is the element whose class name matches the pattern) In the first one however, I can bind the event listener directly to the li elements contained within the ul.
If I change the second jQuery selector to $('[class*="selections"] li') the event listener is never bound to the corresponding lis.
What is causing this behavior?
I am aware that I can just check event.target.tagName to ensure the event is bubbling up from an <li>, but that is not what the question is about.
I originally thought it had something to do with precedence and that the listeners weren't being bound because the lis that would have matched the second selector already matched against the first selector.
However, after implementing logging and looking at the DOM I have determined that when I change the second selector to: $('[class*="selections"] li') neither event listener is bound to the lis that match the second selector.
Here is a link to a JS fiddle of the 'working version'. If you add ' li' to the second selector and then try to click the <li>s in the box to the right, you will see that they no longer become green.
jsFiddle
https://jsfiddle.net/6sg6z33u/4/
Okay, thanks for posting the jsFiddle. This is an easy fix!
The elements in your second li are being added dynamically. When you bind to elements using the shortcut methods like .click() it only binds to the elements on the page when it initially bound
The fix: use the .on() method, which is the preferred method per jQuery foundation. This method allows for live binding meaning it will pick up on dynamic elements.
$('[class*="selections"]').on( 'mousedown', 'li', function(event) {
var opt = event.target;
if (event.which == 1) {
if (opt.className == 'li_unselected') {
opt.className = 'li_selected_2';
} else {
opt.className = 'li_unselected';
}
}
});

jquery not reacting to class changes?

So I've got this assignment I've got to do, it has to do with a christmas tree where you can click the branches and turn them into ornaments and whatnot.
When the tree is on, you should not be able to change the ornaments.
So far i've got this jquery:
$('.branch1, .ornament1, .light1').click(function()
{
alert("WARNING - Power off the tree first!");
die();
});
$('.branch, .ornament, .light').click(function()
{
this.className =
{
light : 'branch', branch: 'ornament', ornament: 'light'
}[this.className];
});
$('#treePowerButton').click(function()
{
$(".branch, .branch1").toggleClass("branch branch1");
$(".ornament, .ornament1").toggleClass("ornament ornament1");
$(".light, .light1").toggleClass("light light1");
$(".powerStatus, .powerStatus1").toggleClass("powerStatus powerStatus1");
});
$('#treeClearButton').click(function()
{
$(".ornament").toggleClass("ornament branch");
$(".light").toggleClass("light branch");
});
But when I turn the tree on and click a branch or ornament it doesn't throw the alert, it just makes the clicked item completely disappear. What am I doing wrong?
here's a jsfiddle: http://jsfiddle.net/a1smjgrv/
Refined Answer:
Try this jsfiddle: http://jsfiddle.net/a1smjgrv/5/. It will accomplish what you're looking for and is much simpler than trying to bind multiple click events on the same element.
Original Answer:
The problem is that when the page is loaded, there are not any 'branch1', 'ornament1', or 'light1' elements on the page, so the click event containing the alert() is not being bound to anything. There are only 'branch' elements on the page, so the click events for the branches are all that's bound.
When the tree is powered ON and the 'branch' elements are changed to be a 'branch1' class, the click event containing the className update are still attached to that element. So, when it tries to update the class name based on your object literal ({light: 'branch', branch: 'ornament', etc...}), it can't find a match for any of those because the class name of the given element is now 'branch1' instead of 'branch', so it's setting the class name on the element to 'undefined' (hence, the disappearance).
If you want a function to be bound to an element at any point in time that the element is rendered to the page, you'll need to bind it at the document level instead. Something like the following:
$(document).on('click', 'branch1', function() {
alert('SHAZAM!');
});

Assign java scripts to cloned HTML element

I have a html div and I clone it using Jquery. That div contains labels and text fields. ids of all of them generated and assigned dynamically. I have no problem with that.
A java script is assigned to a text field of original div. The cloned text fields does not have the javascript assigned to it.
the script I need to assign:
<script>
$(function() {
$("#datepick_onBooking,#datepick_Pay1,#datepick_Pay2,#datepick_totPay,#datepick_deedFees").datepicker();
});
</script>
the script I use to make clones:
<script>
var i = 3;
//When DOM loaded we attach click event to button
$(document).ready(function() {
$('#addAnotherPayment').click(function() {
var cloned = $('.PayDiv0').first().clone();
var noOfDivs = $('.PayDiv0').length+2;
cloned.insertBefore("#totPayForm");
// append count to the ids
cloned.attr('id', 'PayDiv' + noOfDivs);
cloned.find('label').attr('id', 'PayLbl' + noOfDivs);
cloned.find('input[type="text"]').attr('id', 'datepick_Pay'+ noOfDivs);
cloned.find('input[type="number"]').attr('id', 'amount_Pay'+ noOfDivs);
cloned.find('.PayLbl2').html("Payment No " + i++ + ':');
});
});
</script>
datepick_Pay1, datepick_Pay2, datepick_totPay, datepick_deedFees are static elements and they have been assigned to the script. I create text fields using cloning as datepick_Pay3,datepick_Pay4, and so on.
I cannot figure out how to dynamically assign the script to that newly created elements.How can I do that?
A Boolean indicating whether event handlers and data should be copied along with the elements.
change this line.
var cloned = $('.PayDiv0').first().clone(true);
when you clone something especially elements which having events
use parameter as
clone(true)
But this will be harmfull based on how event is attached on the actual element when copying the events to the cloned element may affect the actual.
You need to clone with events. http://api.jquery.com/clone/
var cloned = $('.PayDiv0').first().clone(true);
Then your script needs to be changed to work for dynamic elements. Here as soon as input elements gets focus, asssign the datepicker based on wild card id selector, if it doesn't already have one.
$(function() {
$('body').on('focus',"input[id^=datepick_]", function(){
if(!$(this).hasClass('.hasdatepicker'))
{
$(this).datepicker();
}
});
});

Including variable assignments inside a function?

<button onclick="insert()">Click to insert</button>
<hr id="start">
<hr id="end">
I wrote a javascript function that inserts a div between two horizontal rules:
<script type="text/javascript">
var element = document.createElement("div");
var element_content = document.createTextNode("This is a newly added row!");
element.appendChild(element_content);
var sibling = document.getElementById("end")
var parent = document.getElementById("start").parentNode;
function insert(){
parent.insertBefore(element,sibling);
}
</script>
However when I click the button for the second time, no divs are inserted. I had to include all the variable assignments inside the function in order to click on the button for the 2nd time have the div inserted:
<script type="text/javascript">
function insert(){
var element = document.createElement("div");
var element_content = document.createTextNode("This is a newly added row!");
element.appendChild(element_content);
var sibling = document.getElementById("end")
var parent = document.getElementById("start").parentNode;
parent.insertBefore(element,sibling);
}
</script>
Can someone explain why my first approach didn't allow a 2nd div to be inserted after clicking the button?
In the first example you create one element, since you define it outside of the function. The first time the function is called, that element is appended to the DOM and it won't be appended again (it actually gets removed and appended again, but in the same place):
If the node already exists it is removed from current parent node,
then added to new parent node.
See appendChild on MDN.
The second example creates a new element every time you call the function, and appends that new element to the DOM.
Of course! If you keep the element outside of the function, only 1 new div is created and you are inserting the same div over and over. When a div is inserted in a new location, it is removed from it's old location (if any).
Your second method creates new divs every time the button is clicked.
Because you're only creating a div once and then inserting it when you click the button. You never create a second div, so how can you expect one to magically appear out of nowhere?
The second function works because it creates the div when you click the button, then inserts it. Here you create one div for every click, so it works.
in your first approach, the variables you declare outside the function are created before the click is triggered, therefore they will exist always in the same state independently of what you click.
The first approach only creates the div once and inserts it in the DOM. Calling the function twice doesn't create the div again like the second method does. You could probably take the:
var sibling = document.getElementById("end")
var parent = document.getElementById("start").parentNode;
out of the function though.
The Script is syntactically correct and does what you tell it to do. You're inserting the same node to the same position, which results in... nothing.
Try cloning the node:
function insert(){
parent.insertBefore(element.cloneNode(true), sibling);
}

JQuery Event Listeners in a For Loop

I am trying to create some basic button rollover functionality using Jquery and toggleClass. I am building a list of elements by cloning a DIV from my HTML and duplicating it multiple times (its populating a list of data from a database). To do this I am using a for loop. Here is the currently working code.
var displayNode = document.getElementById('phoneDisplayContainer');
for(var i=0; i<length; i++) {
//Clone the original container display.
var clonedDisplay = displayNode.cloneNode(true);
clonedDisplay.setAttribute('id', 'phoneDisplayContainer' + i);
//Remove hidden class from cloned Element. NOT CROSS BROWSER!
clonedDisplay.classList.remove('hidden');
var children = clonedDisplay.getElementsByTagName('div');
//Fill new nodes children containers with data.
children[1].innerHTML = contact.phone[i].type;
children[2].innerHTML = contact.phone[i].number;
children[3].setAttribute('onclick', 'PhoneUtility.edit(' + i + ');');
children[3].setAttribute('id', 'phoneEditDisplay' + i);
children[4].setAttribute('onclick', 'PhoneUtility.remove(' + i + ');');
//Hidden elements
var hidden = new Array(children[3], children[4]);
//Set rollover events.
clonedDisplay.setAttribute('onmouseover', '$("#' + children[3].id + '").toggleClass("hidden");');
clonedDisplay.setAttribute('onmouseout', '$("#' + children[3].id + '").toggleClass("hidden");');
//Append the new node to the display container
phoneContainer.appendChild(clonedDisplay);
}
}
Is there a way to use Jquery event listeners instead of having to set onmouseover and onmouseout directly on the element?
I tried this:
$(clonedDisplay).mouseover(function() {
$(children[3]).toggleClass('hidden');
});
With no luck. It just displays performs the rollover effect on the last element in the list. This is actually my first attempt at using jQuery so any other suggestions to ways I could jQuery inside the code would be helpful too.
EDIT: I'd also like to toggle multiple children from the arraylist mentioned in the for loop. How would I set this up? I can't seem to pass an array to the jquery command without getting errors.
The following code after your for loop should let you assign all the mouseover and mouseout handlers in one go to apply to all the clones:
$('div[id^="phoneDisplayContainer"]').mouseover(function() {
$(this).find("div").eq(3).toggleClass("hidden");
}).mouseout(function() {
$(this).find("div").eq(3).toggleClass("hidden");
});
// or, given that both handlers do the same thing:
$('div[id^="phoneDisplayContainer"]').on("mouseover mouseout", function() {
$(this).find("div").eq(3).toggleClass("hidden");
})
(If you're using jQuery older than 1.7 use .bind() instead of .on().)
The above says to find all the divs with an id beginning with "phoneDisplayContainer" and assign event handlers. Within the handler, find the fourth descendant div and toggle the class.
You don't show your HTML or CSS, but you could do this all in your CSS if you like. Assuming you can assign a common class ("parentDiv") to the divs that you want to trap the hover event on, and a common class ("childDiv") to their child div (the one to be hidden), you can do this:
.parentDiv .childDiv { visibility: hidden; }
.parentDiv:hover .childDiv { visibility: visible; }
(Obviously you can give more meaningful class names to fit your structure.)
Otherwise, again if you can assign those classes to the appropriate divs then after your loop you can do this with jQuery:
$(".parentDiv").on("mouseover mouseout", function() {
$(this).find(".childDiv").toggleClass('hidden');
});
Basically the same as what I said initially, but using classes for selectors. If you feel like I'm pushing a class-based solution on you that's because I am: it tends to make this sort of thing much easier.

Categories