jQuery dynamic button selector - javascript

Is there a reason this won't work? It works the first time, the variable gets updated (and I can see it), yet it doesn't work on anything after the first one.
$(document).ready(function($) {
var question = 1;
$("ul#question"+question+" li").on( "click", function(e) {
$(this).addClass("test" + question);
question++;
});
});
What I'm trying to do is use the same button actions for 6 different buttons. It has to be this way so that I can use off() on each one, separately, as I go.
Otherwise I have to write the same code out 6 times. Perhaps there's a better way, I just don't understand why this isn't working.
Thanks

incrementing the variable will not change the selector of the event handler. You could use a class selector and then test the id in the event handler:
var question = 1;
$("ul.question li").on( "click", function(e) {
if ($(this).closest("ul").attr("id") === "question"+question) {
$(this).addClass("test" + question);
question++;
}
});
UPDATE
Or even:
var question = 1;
$("ul[id^='question'] li").on( "click", function(e) {
if ($(this).closest("ul").attr("id") === "question"+question) {
$(this).addClass("test" + question);
question++;
}
});

this is just my imagination since i don't know your html looks like.
you just create for 1 action when you have 6 button with different id.
try to use .each with same classname in your button.
var question = 1;
$('.question').each(function() {
$("ul#question"+question+" li").on( "click", function(e) {
$(this).addClass("test" + question);
});
question++;
});

Related

Incorrect selector is called on click event

There is the following JS:
for (var catId in this.data.categories) {
(function (catId) {
$("li#tsCat-" + catId).click(function (event) {
$(this).addClass("tax-active");
event.stopPropagation();
})
}(catId))
for (var floor in this.data.floors) {
(function (floor, catId) {
var selector = "#tsCatFloor_" + catId + "_" + floor;
$(selector).on("click", function (event) {
$(this).addClass("active-filt");
event.stopPropagation();
});
}(floor, catId));
}
}
This code forms two level list.
On clicking links on 1st level, it executes correct click handler $("li#tsCat-" + catId).
But on clicking 2nd level it again executes 1st handler $("li#tsCat-" + catId).
It needs to execute 2nd handler $(selector).
Where is the problem?
Sorry, I didn't provided the full view, but what is good I found a solution what could be useful for everyone:
If You're adding .click() event be sure, that You put the data (against which .click is called) already to DOM. Otherwise .click will never be called.

Why is my JQuery function not triggered on a cloned element when the event becomes "true"?

I have been making this form that must enable the back-end user to create new questions for users to answer. The form is cloned and appended to a div (selector #content) successfully after the first .on(click) event, but it won't duplicate the form again if the cloned button is pressed. The .on(change) event applied to my drop-down selection does change the content of respective divs like it is supposed to, but only on the original form.
Here's the JQuery code:
$(document).ready(function () {
$('.addAnswer').on("click", function () {
var idx = $('#mp div[name^="antwoord"]:last').index() + 1;
$clone = $('#mp div[name^="antwoord"]:first').clone(true, true).attr('class', 'answer content_' + idx);
$('.removeAnswer').show;
$('#mp').append($clone);
$('.answer:last').each(function () {
$('b:last').empty();
$('b:last').prepend(String.fromCharCode(64 + idx) + ". ")
$('.addAnswer').on("click", function () {
idx++;
});
});
if (idx == 2) {
$('.removeAnswer').show();
}
});
$('.nextq').click(function () {
var newqid = $('#content form:last').index() + 1;
$('.done, .nextq, .remove').hide();
$('#content').append('<hr>');
$('#content').append($('form').html()).attr('class', 'q_' + newqid);
$('.nextq').on("click", function () {
newqid++;
});
$('.done:last, .nextq:last, .remove:last').show();
return false;
});
$('.group').hide();
$('#text:last').show();
$('.select:last').on("change", function () {
$('.group').hide();
$('#' + $(this).val() + ':last').fadeIn();
$('button.' + $(this).val() + ':last').fadeIn();
});
});
Because I thought posting the whole HTML template would be a tad bit too much, I provided a JSFiddle for you people.
One extra question for the ones that are feeling kind: In the JQuery code it is seen that the contents of the HTML are being parsed using .html() and appended with .append.(Line 33 on the JSFiddle) As the .on(change) function switches the contents of the divisions it should change, .html() sees those changes and takes those along with it. I'd like the .on(click) function to append the div's content in its original state, unchanged by the changes made beforehand by the back-end user. Any help with this would be much obliged.
In order to have jQuery trigger on new elements you would do something like
$( document ).on( "click", "<your id or class>", function() {
//Do stuff
});

Jquery creating checkboxs dynamically, and finding checked boxes

I have information that comes out of a database and gets put into a list with a checkbox by each element. This is how it is currently done:
function subjects(){
$.ajax({
url: "lib/search/search.subject.php",
async: "false",
success: function(response){
alert(response);
var responseArray = response.split(',');
for(var x=0;x<responseArray.length;x++){
$("#subjects").append("<br />");
$("#subjects").append(responseArray[x]);
$("#subjects").append("<input type='checkbox' />");
}
}
});
}
it works fine, but I need a way to pick up on if a checkbox is clicked, and if it is clicked then display which one was clicked, or if multiple ones are clicked.
I can't seem to find a way to pick up on the checkboxs at all.
the response variable is "math,science,technology,engineering"
Because you are populating the Checkboxes Dynamically you need to Delegate the event
$("#subjects").on("click", "input[type='checkbox']", function() {
if( $(this).is(":checked") ) {
alert('Checkbox checked')
}
});
To better capture the data it is better if you encase the corresponding data into a span , so that it can be easier to search..
$("#subjects").append('<span>'+responseArray[x] + '</span>');
$("#subjects").on("click", "input[type='checkbox']", function() {
var $this = $(this);
if( $this.is(":checked") ) {
var data = $this.prev('span').html();
alert('Current checkbox is : '+ data )
}
});
It would be best to give your dynamically injected checkboxes a class to target them better, but based on your code try:
$("#subjects").on("click", "input", function() {
if( $(this).is(":checked") ) {
// do something
}
});
Since your input elements are added dynamically, you need to use jQuery's .on() function to bind the click event to them. In your case you need to use .on() to bind to an element that exist in the DOM when the script is loaded. In your case, the element with the ID #subjects.
This note from the docs is mainly for machineghost who downvoted my answer for no apparent reason:
Event handlers are bound only to the currently selected elements; they
must exist on the page at the time your code makes the call to .on().
To ensure the elements are present and can be selected, perform event
binding inside a document ready handler for elements that are in the
HTML markup on the page. If new HTML is being injected into the page,
select the elements and attach event handlers after the new HTML is
placed into the page.
$('#subjects input[type=checkbox]').on('click',function(){
alert($(this).prop('checked'));
});
or the change event: in case someone uses a keyboard
$('#subjects input[type=checkbox]').on('change',function(){
alert($(this).prop('checked'));
});
simple fiddle example:http://jsfiddle.net/Dr8k8/
to get the array example use the index of the inputs
alert($(this).prop('checked') +'is'+ $(this).parent().find('input[type=checkbox]').index(this)+ responseArray[$(this).parent().find('input[type=checkbox]').index(this) ]);
simplified example: http://jsfiddle.net/Dr8k8/1/
EDIT: Just for an example, you could put the results in an array of all checked boxes and do somthing with that:
$('#subjects>input[type=checkbox]').on('change', function() {
var checklist = [];
$(this).parent().find('input[type=checkbox]').each(function() {
$(this).css('background-color', "lime");
var myindex = $(this).parent().find('input[type=checkbox]').index(this);
if ($(this).prop('checked') == true) {
checklist[myindex] = responseArray[myindex];
}
});
$('#currentlyChecked').text(checklist);
});
EDIT2:
I thought about this a bit and you can improve it by using .data() and query that or store it based on an event (my button called out by its id of "whatschecked")
var responseArray = ['math', 'science', 'technology', 'engineering'];// just for an example
var myList = '#subjects>input[type=checkbox]';//to reuse
for (var x = 0; x < responseArray.length; x++) {
// here we insert it all so we do not hit the DOM so many times
var iam = "<br />" + responseArray[x] + "<input type='checkbox' />";
$("#subjects").append(iam);
$(myList).last().data('subject', responseArray[x]);// add the data
}
var checklist = [];// holds most recent list set by change event
$(myList).on('change', function() {
checklist = [];
$(myList).each(function() {
var myindex = $(this).parent().find('input[type=checkbox]').index(this);
if ($(this).prop('checked') == true) {
checklist.push($(this).data('subject'));
alert('This one is checked:' + $(this).data('subject'));
}
});
});
// query the list we stored, but could query the checked list data() as well, see the .each() in the event handler for that example
$("#whatschecked").click(function() {
var numberChecked = checklist.length;
var x = 0;
for (x = 0; x < numberChecked; x++) {
alert("Number " + x + " is " + checklist[x] + " of " + numberChecked);
}
});
live example of last one: http://jsfiddle.net/Dr8k8/5/
The general pattern to do something when a checkbox input is clicked is:
$('input[type=checkbox]').click(function() {
// Do something
})
The general pattern to check whether a checkbox input is checked or not is:
var isItChecked = $('input[type=checkbox]').is(':checked');
In your particular case you'd probably want to do something like:
$('#subjects input[type=checkbox]').click(function() {
to limit the checkboxes involved to the ones inside your #subjects element.

A smart toggle class in jQuery

I am trying to implement a script to set different class name on a specific element…
Let's suppose the dom looks like this:
<body class='pre-existing-class-name'>
If I make
smartToogle('body', 'new-class');
// the dom should look like this
// <body class='pre-existing-class-name new-class'>
smartToogle('body', 'new-class-2');
// the dom should look like this
// <body class='pre-existing-class-name new-class-2'>
I did the following code but it does not work:
var smartToogle = function (element, newClassName) {
var oldClassName;
var $element = $(element);
$element.addClass(newClassName);
if (oldClassName !== newClassName) {
$element.removeClass(oldClassName);
}
oldClassName = newClassName;
};
Requirements:
1) I am using query
2) I would like to pass just one class name, the new one.
Solution:
The following code works but I do not like it because it uses global variable.
Any hint to fix it?
function myToggle(newClassName) {
if (window.oldClassName) {
$('body').toggleClass(window.oldClassName);
}
window.oldClassName = newClassName;
$('body').toggleClass(newClassName);
}
You can use data attribute for the element, that is accessible using
$(element).data(attrib_name)
Just a small change is required in your method
function myToggle(newClassName) {
if (window.oldClassName) {
$('body').toggleClass(window.oldClassName);
}
window.oldClassName = newClassName;
$('body').toggleClass(newClassName);
}
can be replaced with
function myToggle(element, newClassName) {
if ($(element).data('oldClassName')) {
$(element).toggleClass($(element).data('oldClassName'));
}
$(element).data('oldClassName', newClassName)
$(element).toggleClass(newClassName);
}
Hope this solves it for you.
Update:
There is one thing you need to understand.
If you want two different behaviors you don't need 2 different classes for the change in behavior.
One is enough, because you can change the behavior based on weither the class is on or off.
Let's say I want my element to have a red hover event in one way.
And want it to have a blue hover event the other way with CSS.
Then this is the way to go:
$('#toggle').click(function(){
$('.normal').each(function(){
$(this).toggleClass('active');
});
});
JSFiddle Demo
Here we use a button to toggle all the divs and change their CSS behavior, looks easy now right?
However if you need to toggle Javascript/jQuery events as well this won't do. In that case you will need to use 3 other methods to manage this; .on(), .off(), and .hasClass().
$('#toggle').click(function(){
$('.normal').each(function(){
if($(this).hasClass('active')){
$(this).off('click');
} else {
$(this).on('click', function(){
alert('You are clicking on an active div.');
});
}
$(this).toggleClass('active');
});
});
JSFiddle Demo 2
As you can see we have added an if statement. If the element has the .active class we turn .off() the .click(). And if there isn't an active class we turn the .click() .on(). Under the if statement we always toggle the .active class. So this doesn't have to be placed inside the if statement.
I hope this clears everything up for you, good luck!
Old Answer:
It is better to use .toggleClass() here.
Use a first class on the element for the default properties and a second like .active for example for the interaction.
Also, using a .on('click', function(){}) bind will make you able to add interaction that will be bound instantly once the element is toggled.
Here's a fiddle: http://jsfiddle.net/NCwmF/2/
I little jQuery plugin for that. Removes the current smart class (if any) and adds the new smart class. If called without parameter className the current smart class gets only removed.
$.fn.smartToggle = function (className) {
var dataId = 'smartToggle';
return this.each(function () {
var $el = $(this);
$el
.removeClass($el.data(dataId) || '')
.addClass(className)
.data(dataId, className);
});
};
​use it like every other jQuery method:
$('body').smartToggle('myClass');
NEW, SIMPLER ANSWER
Works similar to before, with 2 additions: 1.) works if there is no class initially and 2.) works if other functions change the elements class in between calls. I also changed the function name so it doesn't interfere with jQuerys native toggleClass.
$.fn.fancyToggleClass = function(new_class) {
return this.each(function() {
// get the last class this function added (if exists) or false (if not)
var $this = $(this),
toggled_class = $this.data('toggled-class') || false;
// if we dont have an original class, then set it based on current class
if (toggled_class) {
$this.removeClass(toggled_class);
}
// add new class and store as data,
// which we check for next time function is called
$this.addClass(new_class).data('toggled-class', new_class);
// alert the class, just as a check to make sure everything worked!
// remove this for production, or switch to console.log
alert('element class: ' + $this.attr('class'));
});
}
updated fiddle: http://jsfiddle.net/facultymatt/xSvFC/3/
OLD ANSWER
I would suggest storing the original class in the elements data attribute. Then, your function can check if this data is set, and if so clear the elements class adding the original class from the elements data and also the new class you passed in the function.
If data is not set, the function will store the current class as data the first time it runs.
Check out this fiddle for a working example with comments: http://jsfiddle.net/facultymatt/xSvFC/
here is the code. It's a jquery function so it can be called on any element (and is chainable too!)
$.fn.toggleClass = function(new_class) {
return this.each(function() {
// cache selector for this
$this = $(this);
// get original class (if exists) or false (if not)
var original_class = $this.data('original-class') || false;
// if we dont have an original class, then set it based on current class
if (!original_class) {
original_class = $this.attr('class');
$this.data('original-class', original_class);
// we do have an original class, so we know user is now trying to add class
// here we clear the class, add the original class, and add the new class
} else {
// assign the original class, and new class,
// and a space to keep the classes from becoming one
$this.attr('class', original_class + ' ' + new_class);
}
// alert the class, just as a check to make sure everything worked!
// remove this for production, or switch to console.log
alert('element class: ' + $this.attr('class'));
});
}
Hope this helps!
To avoid a global variable you can use data attribute as #ankur writes. Here is a working solution for your problem:
function myToggle(element, newClassName) {
if (!$(element).data('baseclassname')) {
$(element).data('baseclassname', $(element).attr('class'));
}
$(element)
.attr('class', $(element).data('baseclassname'))
.addClass(newClassName);
}
Does this do your job?
var smartToogle = function (element, preExistingClassName, newClassName) {
$(element)[0].className = preExistingClassName + ' ' + newClassName;
};
Just use hasClass. But you'll have to tell the function what both classes are:
function smartToggle(element, class1, class2) {
var $element = $(element);
if ($element.hasClass(class1)) {
$element.removeClass(class1);
$element.addClass(class2);
}
else {
$element.removeClass(class2);
$element.addClass(class1);
}
}
$(function(){
var smartToggle = function (element, newClassName) {
var elementClasses = element.attr('class');
element.addClass(newClassName);
// check if there is more than one class on the element
if(elementClasses .indexOf(' ') >= 0){
var oldClassNames = elementClasses.split(" ");
if (oldClassNames[oldClassNames.length - 1] !== newClassName) {
element.removeClass(oldClassNames[oldClassNames.length - 1]);
}
}
};
smartToggle($('.test'), 'newclass');
smartToggle($('.test'), 'newclass2');
});
Demo - http://jsfiddle.net/Q9A8N/ (look at the console to see what it is doing on each pass)
That should do what you want but as #T.J. Crowder said it is rather fragile and assumes that the class you want to remove is the last one on the element.
As an answer to your question, I would go with ankur's answer
As a follow-up to Sem's answer, regarding the handling of jQuery events :
you can use the on function to handle any jquery event from a parent node, based on a live filter :
function myToggle(element, newClassName) {
if ($(element).data('oldClassName')) {
$(element).toggleClass($(element).data('oldClassName'));
}
$(element).data('oldClassName', newClassName);
$(element).toggleClass(newClassName);
}
//event delegation : 'on' is called on the $('.divContainer') node, but we handle
//clicks on '.divItm' items, depending on their current class
$('.divContainer')
.on('click', '.divItm.plain', function(){ myToggle( this, 'red' ); })
.on('click', '.divItm.red', function(){ myToggle( this, 'blue' ); })
.on('click', '.divItm.blue', function(){ myToggle( this, 'plain' ); });
//initialize each item with the 'plain' class
myToggle( $('.divItm'), 'plain' );
Here is the jsFiddle.
You will note that the function called each time you click on an item depends on its "live" class, and that you don't need to manually enable/disable click handlers each time an item changes class.
You can learn more details from the documentation page.
var smartToogle = function (element, newClass) {
var $element = $(element),
currentClass = $element.data('toggle-class');
if (currentClass != newClass) $element.data('toggle-class',newClass).removeClass(currentClass || '');
$element.toggleClass(newClass);
};
or the other variant:
$.fn.smartToogle = function (newClass) {
currentClass = this.data('toggle-class');
if (currentClass != newClass) this.data('toggle-class',newClass).removeClass(currentClass || '');
this.toggleClass(newClass);
};
In this implementation you'll have to keep the a reference to this instance of fancytoggle.
var fancytoggle = function(el, oldClass){
// create a function scope so we'll have a reference to oldClass
return function(newClass) {
// toggle the old class and the new class
$(el).toggleClass(oldClass+ ' ' + newClass);
// update the new class to be the old class
oldClass = newClass;
};
};
for your example the code would look something like.
var bodytoggle = fancytoggle('body', 'pre-existing-class-name');
bodytoggle('new-class');
// 'new-class' replaces 'pre-existing-class-name'
bodytoggle('new-class-2');
// 'new-class-2' replaces 'new-class'
to see it in action refer to http://jsfiddle.net/aaf2L/6/

How to bind the 'toggle' event to a element

Is there a way I can use the 'bind' syntax for the 'toggle' event handler ?
From the docs I can understand that the correct way is
$('.selector').toggle( function() {}, function() {} );
My problem is that I am removing that element for some reason, and then again add it to the DOM. On adding it again, the toggle does not work.
Thus, I think there should be a way to 'bind' the toggle.
Help please.
This is documentation I am following:
http://jqapi.com/#p=toggle
You must use either .live() or .delegate() to add handlers to all now and future elements.
The problem is that .toggle() binds click, and in complicated cases, like using .live() or .delegate() you have to make your own. From the jQuery Docs:
The .toggle() method is provided for convenience. It is relatively straightforward to implement the same behavior by hand, and this can be necessary if the assumptions built into .toggle() prove limiting.
Roll your own toggle (this can be easily extended to N toggled functions using mod N instead of mod 2 - but then you have to use a switch instead of the JS conditional operator):
var counter = 1;
$('.selector').live("click", function() {
counter++ % 2 ?
// First set of actions :
// Second set of actions;
});
If you want to include 2 functions, then they must be self executing. This would be good for passing arguments in... so the form would be:
var counter = 1;
$('.selector').live("click", function() {
counter++ % 2 ?
(function() { ... }()) : // <== First function
(function() { ... }()); // <== Second function
});
A simple example (no arguments used):
(function() {
var counter = 1;
$('.selector').live("click", function() {
counter++ % 2 ?
$("div").html("First one!") :
$("div").html("Numero dos!");
});
$(function() {
$("body").prepend('<input type="button" ' +
'class="selector" value=" Click Me! " />');
});
}());
try it out on jsFiddle
A more complex example (arguments used):
(function() {
var counter = 1;
$('.selector').live("click", function() {
// Note that the self executing functions are only
// necessary if you want to use arguments.
counter++ % 2 ?
(function() {
$("div").html("First one! Num: " + ($(event.target).index() + 1))
}(event)) :
(function() {
$("div").html("Numero dos! Num: " + ($(event.target).index()+1))
}(event));
});
$(function() {
$elie = $('<input type="button" class="selector" value="Click Me!" />');
$elie.clone().prependTo("body");
$elie.clone().prependTo("body");
$elie.clone().prependTo("body");
});
}());
try it out on jsFiddle
My problem is that I am removing that element for some reason, and then again add it to the DOM. On adding it again, the toggle does not work.
I think, it depends on how exactly you remove element. If you use remove function, all events and jquery data will be removed from element too. And it's opposite with detach function, making it ideal for removing elements you plan to insert back in the document later.
Refer : .bind , .trigger
$(function() {
$('input').click(function() {
$('#id').trigger('toggle')
});
$('#id').bind('toggle', function() {
$(this).toggle()
});
});
Try the Example : http://jsbin.com/ixapo4/2
Try to use live function. Here is documentation. link text
Hope it will help you.

Categories

Resources