I'm trying to iterate over li's in a ul using the jquery .each() method but the click function is not being initiated.
Basic HTML
<div class="row">
<div class="col-md-10 col-md-offset-1">
<div class="product-list">
<ul class="list-inline">
</ul>
</div>
</div>
</div>
Which is populated on pageload with the following jquery function
JQUERY
productLoad: function() {
for (var i = 0; i < state.greens.length; i++) {
$(".product-list ul").append("<li><div class='product'><p> " + state.greens[i].name + " </p></div></li>");
}
},
productLoad is part of a larger object called display and is initiated by display.productLoad();
I'd trying to write a function that calls another function called display.arrange(arg) which causes the page to display a lot of information about the corresponding object in state.greens.
I'm trying to use jquery's .each method in the following way:
JQuery
$(".product-list > li").each(function(index) {
$(this).click(function() {
display.arrange(index);
})
});
The idea is that I add an li for each object in state.greens with productLoad, and then use .each to initiate a function that references each li's corresponding place in state.greens to display information about it.
However, when I click on the li's nothing happens at all, and there are no error messages in the console that correspond to that function so I don't think the click handler is being called.
You can see a working example of what I have on this page: Click to go to page
Scroll down to where it says "Click On A Product To Go To Its Detail Page" and you'll see the ul with each of the stylized li's that represent each product in state.greens.
Try this:
$(".list-inline").on("click", ">li", function(){
display.arrange($(this).index());
});
Reviewing your HTML code, your CSS selector is wrong. Consider that > is used for get immediate children, then your CSS should start selecting the UL tag in order to get all its LI children
Change:
$(".product-list > li")....
By:
$("ul.list-inline > li")...
I believe this is because you're creating (ie, appending) your <li>s using jQuery. That is, they're not in your initial DOM when the page loads. So you need to use jQuery's ".on" to delegate the click event. I'm also not sure that I fully understand what you're trying to accomplish, but I don't think that needs to be in an .each...you should be able to do the following and just use this:
Try switching out your jQuery from this:
$(".product-list > li").each(function(index) {
$(this).click(function() {
display.arrange(index);
})
});
to this:
$(".product-list").on("click", "li", function(index) {
// Run functions, mo' jQuery, etc.
});
That should get your click to register. From there, what you run inside the .on("click") is up to you.
Related
In my web application I use .on() from jQuery to bind click event to a specific li elements in a dynamic multilevel menu created by an AJAX call and passed to the DOM.
$(".dl-menu li:not(:has(li)):not(.dl-back)").on("click", function(){
// My Code...
});
Obviously I put this code behind all the dependencies (jQuery in this case), but I does not work, the code is not being executed.
Only works if I open Firebug and paste the code in it.
I also have tried this:
$(document).ready(function() {
$(".dl-menu li:not(:has(li)):not(.dl-back)").on("click", function(){
// My Code...
});
});
But it does not work too.
What I'm doing wrong?
EDIT: To add more details:
HTML structure:
<div id='tab2' class='col s12'>
<div class='section no-pad-bot' id='index-banner'>
<br><br>
<h2 class='header center green-text'>MENU</h2>
<div id='prodcontainer'>
<div id='dl-menu' class='dl-menuwrapper'></div>
</div>
</div>
</div>
AJAX: (the important parte - the code inside success
$(".dl-menuwrapper").html("<button class='dl-trigger' style='visibility:hidden;'></button>"+json.html.replace('dl-submenu', 'dl-menu dl-menuopen'));
$('#dl-menu').dlmenu();
The json.html contains the string with the HTML to being added to the DOM. Like this:
json.html='<ul class="dl-submenu"><li data-id="17">A<ul class="dl-submenu"><li data-id="18">B<ul class="dl-submenu"><li data-id="20">C</li><li data-id="21">D</li></ul></li><li data-id="19">E</li></ul></li></ul>'
Using .on() this way only binds elements that are currently in the DOM.
Because you load the menu through AJAX, it is not available when this code is executed.
The best workaround is to select a wrapper and specify a selector:
$(".dl-menu").on("click", "li:not(:has(li)):not(.dl-back)", function(){
});
Providing .dl-menu exists in the HTML that you do not create once the document is loaded.
EDIT - Alternative
Place the code setting up the event listener after the menu is loaded.
You should use .on('click'....) on an element that's already present. So you will need to use the code like:
$(document).ready(function() {
$(".dl-menuwrapper").on("click", ".dl-menu li:not(:has(li)):not(.dl-back)", function(){
// My Code...
});
});
Or alternatively,
$(document).on("click", ".dl-menu li:not(:has(li)):not(.dl-back)", function(){
// My Code...
});
I have a little problem here, and if someone could help me, I will truly appreciate.
I have a menu that opens when I click on a div, and once open, I want to close the menu clicking again on te same div. The problem is that I can open the menu but I can't close it.
Here is my code:
<script>
$(document).ready(function () {
$("#menuResp").click(function () {
$('#profile_menu').css('margin-left','0px');
$('#menuResp').css('margin-left','315px');
$('#menuResp').attr('id', 'menuResp2')
});
$("#menuResp2").click(function () {
$('#profile_menu').css('margin-left','-300px');
$('#menuResp2').css('margin-left','0px');
$('#menuResp2').attr('id', 'menuResp')
});
});
</script>
<div id="menuResp">
<ul id="menuRespCss">
<li class="icon-css">
<a>Menu</a>
</li>
</ul>
</div>
Anyone have an idea of why this doesn't work?
What your code is doing is setting callbacks at the moment, when the initial DOM is being built. Of course, there is no #menuResp2 yet. Insdead, set the callback on the document element (or body, or some other parent element), specifying the selector of your menu - this element will fire the event. This will work:
$(document).ready(function () {
$(document).on('click', "#menuResp", function () {
$('#profile_menu').css('margin-left','0px');
$('#menuResp').css('margin-left','315px');
$('#menuResp').attr('id', 'menuResp2')
}).on('click', "#menuResp2", function () {
$('#profile_menu').css('margin-left','-300px');
$('#menuResp2').css('margin-left','0px');
$('#menuResp2').attr('id', 'menuResp')
});
});
But. I would stroungly recommend not to change the ID attribute, but to work with classes instead.
you need to add the click handler like this
$(document).on('click', '#menuResp', function(){
$('#profile_menu').css('margin-left','0px');
$('#menuResp').css('margin-left','315px');
$('#menuResp').attr('id', 'menuResp2');
});
.click() only works for elements that are already created, using .on() will cover elements that will be created later.
you should really be identifying the element by class though , and using .addClass() and .removeClass() like the comment suggest
just use toggle. if the item is closed, it will open, if its open, it will close. all the answers above do not check to see if the item is open already or not.
$(document).on("click", function(e){
e.preventDefault();
$('#menuResp').toggle();
$("##menuResp2").toggle();
})
easier would be to give element a single class and just toggle once on class name, rather than changing the ID of the item, this second item would not have an avent binding added to it. But at the same time. You dont need the ID when you can just toggle with class. like so:
$(".clasname").toggle();
this will open any element that is closed with the class of clasname. this will also, at the same time, close all elements that have that class name, and are also open
I'd like the ability to nest one plugin within another. However my selectors are too aggressive and keep retrieving the elements for the nested plugin aswell.
For example given the following HTML:
<div class="my-plugin">
...
<div class="my-plugin">
...
<button class="select">Select</button>
</div>
</div>
With the following code to create the plugin:
$(function() {
$('.my-plugin').myPlugin();
});
When I say the following (within my plugin):
// element is the element the plugin is attached to
$('.select', element);
This will retrieve the select element from the nested plugin within the outer plugin but I'd like it not to. Also I'd like to do the same when attaching click events. For example the following code should only attach the click event in the nested plugin and not within the outer plugin.
element.on('click', '.select', function(e) {
...
});
I hope I've explained that clearly. I'd appreciate if someone could show me how my selector can be improved to handle this. Thanks
The problem is, selectors work against the context they're given. If you tell jQuery to search a div, it will search everything in that div for what it's looking for. It's just how jQuery works.
If you want to exclude the inner plug-in, give it an id and exclude it using .not(). Or you could give it a class or data-* attribute as well. We just need something to tag it as "do not include".
So, do this:
$('.select', element).not('#mySecondPlugin');
or:
$('.select', element).not('.mySecondPlugin');
or:
$('.select', element).not('[mySecondPlugin="true"]');
This selector will select everything within your outer element EXCEPT the inner one and its contents.
And finally:
$('.select', element).not('[mySecondPlugin="true"]').on('click', function(e) {
...
});
You can use jQuery .closest() to find the first occurrence of a selector from an element. So you could target the nested div with #('.select').closest('.my-plugin').
Using jQuery .filter():
var myPlugin = this;//or whatever is representing your plugin jQuery object.
var selectsYouWant = $('.my-plugin .select').filter(function(index){
if(this.closest('.my-plugin') === myPlugin) {
return true;
} else {
return false;
}
});
You need to understand events. When you click on the element, event bubbles up the DOM tree. You need to stop propagation, so that it would not reach outer plugin handler. Depending on the logic you may also need to prevent default action:
element.on('click', '.select', function(e) {
e.stopPropagation();
e.preventDefault();
// ...
});
Also, not sure what is the logic inside plugin, but you can filter out inside items:
var button = $('.my-plugin').find('.select').not('.my-plugin .my-plugin *');
button.css('color', 'red');
See: FIDDLE
This is the approach I recommend.
At initialization:
$(element).addClass('my-plugin');
var $selects = $(element).find('select')
.not( $(element).find('.my-plugin select') );
You would have to make sure that the element and $selects variables are accessible to all functions in the plugin.
On the note about on(), here's what I would suggest:
element.on('click', '.select', function(){
// see if the closest .my-plugin is the matching element, and not
// a child plugin
if ( ! $(this).closest('.my-plugin').is( element ) )
return;
...
});
Try to start outside of your first plugin:
for example:
<div class="plugin-wrapper">
<div class="my-plugin">
...
<button class="select">Select</button> //We want this one
<div class="my-plugin">
...
<button class="select">Select</button> //Without this one
</div>
</div>
</div>
You would then be able to use something like $('.plugin-wrapper > .my-plugin > .select') which would get ONLY the first .select without the second. Which I believe is what you are trying to accomplish
For the onclick
$('.plugin-wrapper > .my-plugin > .select').on('click', function () {
//Your code here
});
This is probably a very common question, but I was unable to find an answer myself;
All my list elements call the function setQuery like this
onClick="admin_stats.setQuery(this);"
rather than [hardcode] add this to every list element, is there a way to simply have it run when a list element is clicked?
I'm not very familiar with jQuery Live or binding/unbinding, but I think they would play a role here?
Before I reinvent a rather square-looking wheel I thought I might ask =)
edit: my list elements look like
<ul>
<li id="usersPerMonth" onClick="admin_stats.setQuery(this);">Users per Month</li>
<li id="statsByUser" onClick="admin_stats.setQuery(this);">Stats by User</li>
</ul>
the this.attr("id") is then used to look up what the actually SQL text looks like, from a json-style variable:
queries : {
usersPerMonth : " ... some sql ...",
statsByUser : " ... some sql ...",
...
}
so that's why I have the divs named that way (and I'm open to design suggestions)
$(function() {
$('#myList').delegate('li', 'click', function() {
admin_stats.setQuery( this );
});
});
This assumes your <ul> element has the ID myList. It will handle clicks inside of it on any <li> elements, calling your function, and passing this as the argument.
The .delegate() code is wrapped in $(function() {}); so that it doesn't run until the DOM is ready. This is a shortcut for jQuery's .ready() function.
Yes - use jQuery like this:
$('li').live("click", function(event){
admin_stats.setQuery(event.target);
});
This is assuming you want to set it to every li element. You can find the documentation for live here: http://api.jquery.com/live/
What live does makes sure that all elements passed to it will always have the click handler in the function specified.
jQuery is probably your best bet. Are you adding new elements on the fly, or will the elements be there when the attach is ready to go? If they're "satic" in the sense that once the page is loaded that's it, you could use:
$(document).ready(function(){
$('li').bind('click',function(e){ // may need to change the selector to be more specific
admin_stats.setQuery(this);
});
});
that needs to fire onClick... so:
$('li').click(function(){
admin_stats.setQuery($(this));
});
In jQuery you select elements using CSS-style selectors (can use this if more elements will not be added; this uses .click()):
$(function() {
$('#liContainer li').click(function() {
admin_stats.setQuery(this);
});
});
We put the whole thing inside $(function() { ... }); because the page needs to be ready before binding the event handler. Alternatively, you could do it like this if more elements are added. This uses .delegate():
$(function() {
$('#liContainer').delegate('li', 'click', function() {
admin_stats.setQuery(this);
});
});
I'm using this function to create an transparent overlay of information over the current div for a web-based mobile app.
Background: using jQTouch, so I have separate divs, not individual pages loading new.
$(document).ready(function() {
$('.infoBtn').click(function() {
$('#overlay').toggleFade(400);
return false;
});
});
Understanding that JS will run sequentially when i click the button on the first div the function works fine. When I go to the next div if I click the same button nothing "happens" when this div is displayed, but if i go back to the first div it has actually triggered it on this page.
So I logically duplicated the function and changed the CSS selector names and it works for both.
But do I have to do this for each use? Is there a way to use the same selectors, but load the different content in each variation?
Would something like this work? I'm assuming what you want is for different buttons to call toggleFade on different overlay divs.
function makeOverlayHandler(selector) {
return function() {
$(selector).toggleFade(400);
return false;
}
}
$('button selector').click(makeOverlayHandler('#overlay1'));
$('another button selector').click(makeOverlayHandler('#overlay2'));
You could also change makeOverlayHandler's selector parameter to a jQuery object instead of a selector and call it like this: makeOverlayHandler($('#overlay1')).
This best way to do this is to make it all relative, so say you have markup like this:
<div class="container">
<div class="overlay">Overlay content</div>
<button class="infoBtn">Click to show overlay</button>
</div>
Then you can find the overlay for this button realtively, like this:
$(function() { //equivalent to $(document).ready(function() {
$('.infoBtn').click(function() {
$(this).closest('.container').find('.overlay').toggleFade(400);
return false;
});
});
You can optimize this further, e.g. .children('.overlay') if the overlay is always a direct child of container. This goes from the current button (this), finds the .container it's in using .closest() and then finds the .overlay inside of it using .find(). With this approach you have one small bit of code to handle all your bindings, much easier to maintain :)