jQuery class selector not selecting after class change - javascript

Im using Javascript to build a button click and puzzle adventure game. The game will allow a series of button commands. When the "go" command is clicked, the buttons change to different exits, and the class is changed to exit, like so:
function setExitButtons(){
clearButtons();
for (var i = 0; i < player.currentRoom.exits.length; i++) {
var buttoni = button[i];
buttoni.className = "exit";
buttoni.innerHTML = player.currentRoom.exits[i].name;
$(buttoni).show();
}
}
Where clearButtons hides all of the buttons so only the correct ones show, and button[] is the nodelist for the buttons.
The class does change when this function is called.
I then have another jquery function with a class selector, like so:
$(".exit").click(function(){
//roomchangefunction
});
The .exit function is not activated when the button with exit class is clicked. I have a document ready function encompassing the whole part. Thoughts?

This only runs once, when the document loads:
$(".exit").click(function(){
//roomchangefunction
});
At that time, there are no matching elements for .exit. So no click handlers are assigned. After that, this never runs again.
Since the elements are dynamically changing, I recommend binding a click handler to a common parent element using .on() instead. Something like this:
$(document).on('click', '.exit', function () {
//roomchangefunction
});
The difference is that the click event is actually assigned to a common parent (in this case document, though any common parent element will work such as a div which always contains the .exit elements). When an element is clicked, the "click" event occurs on that element and all the way up the DOM. So this handler would be invoked. The second argument is a filter, so it looks for elements which match that filter when invoking the handler function.
That way the filter for .exit happens when the element is clicked, rather than when the document is loaded, so that elements which are dynamically changed during the life of the document are still handled.

You're adding the class name after the page has loaded so need to use .on() with delegated events. Basically it means, the event is bound to the parent (in this case document), but affects the designated children (in this case .exit).
$(document).on('click', '.exit', function(){
//roomchangefunction
});

You need to use .on() like so:
$(".exit").on("click", function(){
//roomchangefunction
});

Related

jQuery won't listen for my second click

i'm trying to register the second click on a link, by adding a new class and finding it with jQuery. But it won't change the class after the 1st click.
Hope it makes sense and thank you in advance.
// Listen for when a.first-choice are being clicked
$('.first-choice').click(function() {
// Remove the class and another one
$(this).removeClass('first-choice').addClass('one-choice-made');
console.log('First Click');
// Some code goes here....
});
// Make sure the link isn't fireing.
return false;
});
// Listen for when a.one-choice-made are being clicked
$('.one-choice-made').click(function() {
// Remove the class and another one
$(this).removeClass('one-choice-made').addClass('two-choice-made');
console.log('Second Click');
// Some code goes here....
});
// Make sure the link isn't fireing.
return false;
});
At load, .one-choice-made does not exist, so when you call $('.one-choice-made'), it returns an empty jQuery object, hence the click() handler is not added to anything.
What you want to do is attach the handler to something that will always exist, which will respond to the click event (i.e. a parent/ancestor element). This is what $.on() will do for you when called in a delegated handler syntax (i.e. with a filter selector):
$(document).on('click', '.one-choice-made', function() {
// my second function
}
In this case, jQuery attaches a special handler to document, which watches for click events that propagate to it from children elements. When it receives a click, jQuery looks at the target of the click and filters it against the selector you provide. If it matches, it calls your function code. This way, you can add new elements with this class at any time, as long as they are children of the elements from the selector(s) you applied .on() to. In this case, we used document, so it will always work with new elements.
You can pare this down to a known permanent parent element to reduce click events, but for simple cases document is fine.
NOTE: In the same way, removing the class first-choice will not have any affect on whether the first click handler is called, because the handler is applied to the element. If you remove the class, the element will still have the handler. You will need to use a delegated handler for that as well:
$(document).on('click', '.first-choice', function() {
// my first function
}
Demo: http://jsfiddle.net/jtbowden/FxqX9/
Since you're changing the class you need to use .on()s syntax for delegated events.
Change:
$('.one-choice-made').click(function() {
to:
$(document).on('click', '.one-choice-made', function() {
Ideally you want to use an element already in the DOM that's closer than document, but document is a decent fallback.

JQuery on() usage and overwriting

I am working on a mobile app, and I am currently using the on() method to implement a swipe-to-delete feature (I understand that there are libraries that would allow me to do this, and am open to any thoughts you have on the merits of different options). I have code that looks like this:
var favArticles = $('#favoritesList li');
favArticles.each(function(i, li){
var id = $(li).attr('id');
$(li).on("swipeLeft",function(){
//console.log('SwipeLeft ' + id);
var html = $(li).html();
var button = '<div ><button onclick="favDelete(id, i)">Delete</a></div>';
$(li).html('<div style="position:relative;">' + html + button + '</div>');
});
});
I am trying to manage a mutable list of articles that, so whenever I render the favorites list, I grab all the current articles, and bind a swipe event to them. If swiped, a button is brought up on top of the article, and when the user hits the button a function runs that removes the swiped li from the list, and deletes it from the stored favorites.
Within favDelete, I use the index i to remove() the correct li. This means that I need to recreate all the events with updated values of i each time an element is deleted.
So, my question: if I call on() again, for the same event on the same DOM element, will the old binding be overwritten? Or do I create a memory leak by constantly adding new on() actions to my list elements?
UPDATE: Yes, JQuery, not Javascript. Apologies. And I know that my favDelete call won't work as it is shown, I omitted chopped a bunch of quotation marks out for the post to try to improve readability.
I would just use two event handlers and event delegation: One for the swipe event and one for the click on the delete button.
I don't know if swipeLeft works with event delegation, but even if not, it would not change much:
$('#favoritesList').on('swipeLeft', 'li', function() {
// show delete button
// or $(this).html(...)
$(this).append('<div class="deleteButton"><button>Delete</a></div>');
}).on('click', '.deleteButton button', function() {
// find ancestor li element
var $li = $(this).closest('li');
// and pass it to favDelete
favDelete($li);
// if you don't remove the element in the favDelete, do it here:
$li.remove();
});
Using event delegation for the delete buttons makes the most sense, since you are "constantly" adding and removing them.
Learn more about event delegation.
All the styling you can do with a CSS rule for the deleteButton class. You'd also have to change your favDelete method to accept a li element (or rather a jQuery object with a li element) instead of an ID and index.
if I call on() again, for the same event on the same DOM element, will the old binding be overwritten?
.on() will always add a new event handler. In your code, you even create a new event handler function for every list element, which is indeed a waste of memory.
In my code above, there are only two event handlers for all li and button elements.
Two other ways you could do this, rather than a hard-coded index:
Use the ID to select the correct LI element (in your favDelete function)
Pass in a selector instead of the index (i.e. $(this).closest('li'))
If You'll call 'on' again, previous bindings will still be alive - for ex. If You'll call few times (let's say 5 times) on('click', function() { console.log('fired'); }. When You'll click one time on the object it'll thorw 'fired' five times.

Click function not working on div class created on the fly

I am unsure as to how to approach this problem.
I have some buttons made in the HTML with some data attributes added to them. These buttons have a class called roleBtn which will call my jQuery roleBtnClicked function and grab the variables in the HTML's data attributes.
$(".roleBtn").click(roleBtnClicked);
function roleBtnClicked(event){
reg.roleName = $(this).html(); /* Get role name: Actor */
reg.blueBtn = $(this).data('blue'); /* Get blue-btn-1 */
reg.the_Num = $(this).data('num'); /* Get the number */
reg.modal_ID = $(this).data('modal'); /* Get modal-1 */
Now using this information, after the roleBtn is clicked a modal window will come up, I then have a doneButton which will close the modal window and use the variables from the data attributes to then generate new HTML on the fly. This new HTML will contain a button with the class of blueBtn.
My problem is that my click function for blueBtn won't work on a blue button that was created on the fly. It will work on a div that already has the class blueBtn before hand, but doesn't work if it was created on the fly.
Do you know a workaround to this? Or am I missing something simple?
After the doneButton is clicked I have another function that creates the new HTML including the blueBtns on the fly:
$('.selected_row .choices-col-left').append('<div class="blueBtn-holder" id="blueBtnHolder-'+theNum+'"><div class="blueBtn" id="'+blueBtn+'" row="'+rowName+'" modal="'+modal_ID+'" num="'+theNum+'">'+roleName+'</div></div>');
My blue button click function which doesn't work
$(".blueBtn").click(blueBtnClicked);
function blueBtnClicked(event){
alert("Where are you blueBtn on the fly?");
console.log("Where are you blueBtn on the fly?");
};
Try this:-
You need Event delegation for dynamically created elements using .on()
$(".selected_row").on('click', '.blueBtn', blueBtnClicked);
function blueBtnClicked(event){
alert("Where are you blueBtn on the fly?");
console.log("Where are you blueBtn on the fly?");
};
Demo
When you just bind a click event, it will be only bound to the existing DOM elements, so you want to bind the event to the parent element and later any elements you add with the same selector with that container will have the event available by delegation.
From Jquery Docs
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. This element could be the container element of a view in a Model-View-Controller design, for example, or document if the event handler wants to monitor all bubbling events in the document. The document element is available in the head of the document before loading any other HTML, so it is safe to attach events there without waiting for the document to be ready.
That's because you have to reassign event handlers for newly created items.
A better approach would be using .on on the container object, and then specify the child objects that should respond to the clicks:
$(".container").on('click', '.blueBtn', blueBtnClicked);
This way even if you ad objects on the fly, they will still respond. This is actually a lot more efficient way of handling that, because you only create one event handler as oppose to many. This is actually called event delegation.
Remember that when you do $(selector).click, you're telling jQuery to find all elements matching selector and assign the specific "click handler" to them. This does not happen again when you create new objects, because you're not telling jQuery to handle every future object as well (jQuery will not be aware of you adding a new button and will not assign a handler to it).

jQuery event delegation

I need some help with the callbacks. For some reason, they don't work really well.
I'm making a game with jQuery. I have a <div id='button'></div> for all the buttons that are going to be in the game. The game is going to have two buttons that make actions, and a question on top of it. The question is controlled by a <h3 id='text'></h3>. What I want to know, is that for some reason I can't set callback functions to the button's ID's. In example,
I'd have the yes or no, that have their own id's set through jQuery like this:
$('#button').html('<button id='yes'>Yes</button><button id='no'></button>');
But for some reason, I would be able to set this:
$('yes').click(function(){
//function I would want
});
Of course, that's not what my code has, that was just an example. Here's the real code:
$(document).ready(function(){
$('#main,#batman,#car,#cop,#hobo,#knife,#gangfight,#ganggun,#gangknife,#blood,#hr').hide(-100);
var hr=$('#hr');
var main=$('#main');
var batman=$('#batman');
var car=$('#car');
var hobo=$('#hobo');
var cop=$('#cop');
var knife=$('#knife');
var gangfight=$('#gangfight');
var ganggun=$('#ganggun');
var gangknife=$('#gangknife');
var blood=$('#blood');
var text=$('#text');
var button=$('#button');
$('#start').html('Are you ready to play?');
$('#button').html('<button id="yes">Yes</button><button id="no">No</button>');
$('#yes').click(function(){
$('#yes,#no').hide(function(){
$('#start').hide();
main.fadeIn(-100);
hr.fadeIn(-100,function(){
text.delay(1000).html("You were just wandering around in the streets of new york, when suddenly.. You see batman!! You've never really liked him, what do you do?")
button.html('<button id="fight">Fight</button><button id="leave">Leave</button>',function(){
batman.fadeIn(1000);
$('fight').click(function(){
});
$('leave').click(function(){
text.fadeOut(function(){
text.text('Good call. As you leave, you encounter a hobo. What do you do?');
});
});
});
});
});
});
$('#no').click(function(){
$('#yes,#no').hide();
$('#start').text('Oh, okay then. Come back later!');
});
});
I'm just wondering.. How can I set callback functions to the 'fight' and 'leave'.
If you're wondering why there's all these variables at the start, those are just the images and characters.
You can't set a click handler on an element that doesn't exist. What you should do is use .on to bind a element further up the tree. Something like:
$("#someparentelement").on("click", "#yes", function() {
// your code
});
Which version of jQuery are you using? You should probably use jQuery.on() in this situation since your click handler code probably gets executed before the button is actually available in the DOM.
$("#button").on("click", "#yes", function (event) {
// Your yes-button logic comes here.
});
For more details and possibilities, read about the .on(events [, selector ] [, data ], handler(eventObject)) method in the jQuery documentation:
If selector is omitted or is null, the event handler is referred to as direct or directly-bound. The handler is called every time an event occurs on the selected elements, whether it occurs directly on the element or bubbles from a descendant (inner) element.
When a selector is provided, the event handler is referred to as delegated. The handler is not called when the event occurs directly on the bound element, but only for descendants (inner elements) that match the selector. jQuery bubbles the event from the event target up to the element where the handler is attached (i.e., innermost to outermost element) and runs the handler for any elements along that path matching the selector.
In this case, you want to delegate the event since your element is not yet available in the DOM when you're binding the event.
Don't use the click(), use on('click') and attach it to the document.
Creating a handler this way, will ensure that any new elements will be able to trigger the event.
$('fight') selects fight tag, not the tag with fight id. Try to use $('#fight') instead.

Does jQuery overwrite event listeners?

I've got a bunch divs which each contain a remove link attached with the click event below:
var observeRemoveRoom = function
$('.remove_room').click(function(){
$(this).parent().removeClass('active');
});
}
Clicking it removes the 'active' class of the parent (the div). I call this observeRemoveRoom function on window load which works fine.
The thing is, I have another function which adds more of the same divs. Since the a.remove_room links contained within the new divs weren't around on window.load I need to call observeRemoveRoom.
Am I somehow duplicating the event handlers? Does jQuery overwrite them? If so should I unbind the handlers?
Each time you call observeRemoveRoom jQuery will add a new unique event handler function for a click event.
So yes, you need to .unbind() either all currently bound handlers by just calling .unbind() without arguments, or be specific and pass in a function reference.
You can try a live query to keep them updated: http://docs.jquery.com/Plugins/livequery
Yes, you will be duplicating the event-handlers if you call observeRemoveRoom again, but it might not be noticeable since you are only calling the removeClass method which does nothing if the class is not found, which would be the case after the first listener is triggered.
Instead you can un-bind and re-bind the click event each time, like:
var observeRemoveRoom = function(){
var remove_class = function(){
$(this).parent().removeClass('active');
};
$('.remove_room').off('click', remove_class).on('click', remove_class);
}
But that said, it is recommended that you do this outside this function`, rather than binding and unbinding the event every time, like:
$(document).ready(function(){
var remove_class = function(){
$(this).parent().removeClass('active');
};
// If the element exists at dom ready, you can bind the event directly
$('.remove_room').on("click", remove_class);
// If the element is added in dynamically, you can [delegate][1] the event
$('body').on("click", '.remove_room', remove_class);
// Note: Although I've delegated the event to the body tag in this case
// I recommend that you use the closest available parent instead
});
http://api.jquery.com/on/#direct-and-delegated-events : [1]

Categories

Resources