I am currently populating a list dynamically based on some web calls. When these calls return I use an HTML template to add another item to the list based on the call response. I also am trying to attach some events to these objects using jQuery, but when I generate the items it seems like only the last item generated has the appropriate events firing.
So here is the code:
The javascript generates the html string and the adds it to the list:
List.innerHTML += html;
The html is generated using moustache.js and renders properly, I set the id in the template before adding it.
The template has this form:
var template = '<li style="width:400px; height:100px" id="{{id}}" ><img src="{{source}}" style="float:left"/>{{title}} <button id="{{id}}button">Delete</button></li>';
Where all the elements are replaced appropriately.
Then I try to attach a click event to the button and a double click event to the entire list object:
$('#' +id +'button').bind('click', function() {
//SOME STUFF GOES HERE
});
$('#' +id +'button').bind('dblclick', function() {
//MORE STUFF GOES HERE
});
Im not sure why the event wouldnt attach to each object, am I missing something?
Thanks!
The DOM might not have been updated on the browser by the time you add the click handlers. You have a couple options:
Use delegated binding (using .on)
Attach the events before adding them to the DOM
Example 1:
$('body').on('click', '#' +id +'button', function () { /* foo */ });
Example 2:
$('#' +id +'button', myHtml).on('click', function () { /* foo */ });
List.append(myHtml);
Related
On page load, I have a search box that, once used, populates a div with multiple images. The javascript from the search uses this function to append all images into the div
function appendSomeItems(url, id, name, style) {
return '<div><div class="md-card md-card-hover"> <div id="getImage" class="gallery_grid_item md-card-content"> <img class ="uk-align-center imageClick"></a> <div class="gallery_grid_image_caption"> <span class="gallery_image_title uk-text-truncate">' + name + '</span> <span>' + style + '</span> </div></div></div></div>';
}
This works perfectly. Now I'm trying to make it so that when I click any one of the images it triggers an action (in this case a console log)
$('.imageClick').click(function handleImage() {
console.log(good);
});
However, it does nothing. No error but no console log.
What am I doing wrong here?
You need to use event-delegation in order to bind an event to dynamically created elements:
This approach uses document as the parent element, however, a good practice is to use the closest parent element.
$(document).on('click', '.imageClick', function handleImage() {
console.log(good);
});
Try with .on() to attach event on dynamically created element. This will allow attaching the event to the elements that are added to the body at a later time:
$('body').on('click', '.imageClick' function handleImage() {
console.log(good);
});
The problem is that you are calling $(".imageClick").click() before you dynamically create the items.
This means that jQuery doesn't actually bind the click listener to the items, since when $(".imageClick").click() is run, the elements don't actually exist yet.
Try this:
$("body").on("click", ".imageClick", function handleImage() {
console.log("good");
});
Also see this post for more information: In jQuery, how to attach events to dynamic html elements?
I'm stuck with a situation where my DOM elements are generated dynamically based on $.getJSON and Javascript functions for this elements are not working. I'll post some general idea on my code because I'm looking just an direction of what should I do in this situation.
site.js contains general features like
$(document).ready(function() {
$('.element').on('click', function() {
$(this).toggleClass('active');
});
$(".slider").slider({
// some slider UI code...
});
});
After that:
$.getJSON('json/questions.json', function (data) {
// generating some DOM elements...
});
I have also tried to wrap all site.js content into function refresh_scripts() and call it after $.getJSON() but nothing seems to be working.
Firstly you need to use a delegated event handler to catch events on dynamically appended elements. Then you can call the .slider() method again within the success handler function to instantiate the plugin on the newly appended content. Try this:
$(document).ready(function(){
$('#parentElement').on('click', '.element', function() {
$(this).toggleClass('active');
});
var sliderOptions = { /* slider options here */ };
$(".slider").slider(sliderOptions);
$.getJSON('json/questions.json', function(data) {
// generating some DOM elements...
$('#parentElement .slider').slider(sliderOptions);
});
});
Instead of calling on directly on the element, call it on a parent that isn't dynamically added and then use the optional selector parameter to narrow it to the element.
$('.parent').on('click', '.element', () => {
// do something
});
The difference between this and:
$('.element').on('click', () => {});
is with $('.element').on(), you're only applying the listener to the elements that are currently in that set. If it's added after, it won't be there.
Applying it to $(.parent), that parent is always there, and will then filter it to all of it's children, regardless when they're added.
the easiest way is to add this after you add/generate your DOM
$('script[src="site.js"]').remove();
$('head').append('<script src="site.js"></script>');
of course your js function that generates DOM needs to be on another file than your site.js
I am using a server side technology (JSP) to render my views. Therefore I have a for loop that loops through a list. Now, I want to bind a click listener to a button that is rendered for each item in the list. The code in the click listener needs the item ID, which I don't understand how to pass to the JavaScript code.
I also use requirejs, if that matter to the solution.
Maybe you just don't need to pass the html ID to your javascript. You can use a css class to retrieve your buttons in your DOM. The javascript code depends of what framework you use.
In native javascript, you can retrieve your buttons like this :
var buttons = document.querySelectorAll('button.my_class');
Just replace my_class by what you want.
You can access a button id by doing this :
var button_id = buttons[0].id
If you want to attach the item id to the button, you can use a data attribute. For example :
<button id="my_button" data-id="item_id" />
In the javascript, you can access to data_id like this :
var item_id = buttons[0].getAttribute('data-id');
In most event handlers, this is the DOM node that triggered the event, so something like this works:
button.onclick = function() {
alert(this.id);
}
Here is the live example
If you were using jQuery and requirejs this would be like this:
require(['jQuery'], function ($) {
$('.container_element').on('click', '.button_class', function () {
console.log('You have clicked button with id ', $(this).attr('id'));
})
})
I have a list of items that are filled dynamically:
function ViewData(data) {
var SI = (typeof data) == 'string' ? eval('(' + data + ')') : data;
$('#ListContainer').empty();
for (var i = 0; i < SI.length; i++) {
var text = '<a href="Page.htm" rel="external" onclick= "SaveData();"
class="lesson" LessonID="' + SI[i].lessonID
'"><span class="lesson_subject">' + SI[i].sbj_Name +
'</span></b></a>
$('#ListContainer').append(text).trigger("create");
}
}
When one item of them is clicked, the page should navigate to another page carrying the data of this link.
I made this function to save the values of found in the link:
function SaveData() {
localStorage["LessonID"] = $('#ListContainer').find('.lesson').attr('LessonID');
localStorage["SubjectName"] = $('#ListContainer').find('.lesson_subject').text();
}
but it saves "All of the data". I want to save the data of the selected item only.
Do you have any ideas ?
Thanks.
It saves all of the data because your find('.lesson') will find every link inside #ListContainer. I would suggest removing your inline event handlers and taking advantage of event delegation with the on method:
$("#ListContainer").on("click", ".lesson", function() {
localStorage["LessonID"] = $(this).data("lessonid");
localStorage["SubjectName"] = $(this).data("subject");
});
Notice that I've used the data method instead of attr. That will require another change to your markup:
<a href="Page.htm" rel="external" class="lesson" data-lessonid="someID" data-subject="someSubject">
This uses HTML5 data-* attributes, which are the recommended approach when it comes to storing arbitrary data on an element.
Why use event delegation? It is more efficient, as it results in less event handlers bound to elements. Instead of one on every a.lesson element, there is just one on the #ListContainer element.
Because most DOM events bubble up the tree, a click on a descendant of #ListContainer will bubble up through the DOM tree, eventually reaching #ListContainer. The jQuery on method will pick up the event at this element, and check to see if it originated at an element matching .lesson. If so, it will execute the event handler.
Change this
onclick= "SaveData();"
to
onclick= "SaveData(this);"
Then change the saveData function to
function SaveData(elem) {
localStorage["LessonID"] = $(elem).attr('LessonID');
localStorage["SubjectName"] = $(elem).find('.lesson_subject').text();
}
Or even better, you can use $.on like #James Allardice showed in his answer.
You're mixing old world JavaScript and jQuery.
Use $("<a ...") to create your element, append it and then attach a .click handler which can use $(this) when it is fired. Use .data to add data to the elements when you add them which the .click handler can utilise.
The function associated with the selector stops working when I replace it's contents using .html(). Since I cannot post my original code I've created an example to show what I mean...
Jquery
$(document).ready(function () {
$("#pg_display span").click(function () {
var pageno = $(this).attr("id");
alert(pageno);
var data = "<span id='page1'>1</span><span id='page2'> 2</span><span id='page3'> 3</span>";
$("#pg_display").html(data);
});
});
HTML
<div id="pg_display">
<span id="page1">1</span>
<span id="page2">2</span>
<span id="page3">3</span>
</div>
Is there any way to fix this??...Thanks
Not sure I understand you completely, but if you're asking why .click() functions aren't working on spans that are added later, you'll need to use .live(),
$("#someSelector span").live("click", function(){
# do stuff to spans currently existing
# and those that will exist in the future
});
This will add functionality to any element currently on the page, and any element that is later created. It keeps you have having to re-attach handlers when new elements are created.
You have to re-bind the event after you replace the HTML, because the original DOM element will have disappeared. To allow this, you have to create a named function instead of an anonymous function:
function pgClick() {
var pageno = $(this).attr("id");
alert(pageno);
var data="<span id='page1'>1</span><span id='page2'> 2</span><span id='page3'> 3</span>";
$("#pg_display").html(data);
$("#pg_display span").click(pgClick);
}
$(document).ready(function(){
$("#pg_display span").click(pgClick);
});
That's to be expected, since the DOM elements that had your click handler attached have been replaced with new ones.
The easiest remedy is to use 1.3's new "live" events.
In your situation, you can use 'Event delegation' concept and get it to work.
Event delegation uses the fact that an event generated on a element will keep bubbling up to its parent unless there are no more parents. So instead of binding click event to span, you will find the click event on your #pg_display div.
$(document).ready(
function()
{
$("#pg_display").click(
function(ev)
{
//As we are binding click event to the DIV, we need to find out the
//'target' which was clicked.
var target = $(ev.target);
//If it's not span, don't do anything.
if(!target.is('span'))
return;
alert('page #' + ev.target.id);
var data="<span id='page1'>1</span><span id='page2'>2</span><span id='page3'>3</span>";
$("#pg_display").html(data);
}
);
}
);
Working demo: http://jsbin.com/imuye
Code: http://jsbin.com/imuye/edit
The above code has additional advantage that instead of binding 3 event handlers, it only binds one.
Use the $("#pg_display span").live('click', function....) method instead of .click. Live (available in JQuery 1.3.2) will bind to existing and FUTURE matches whereas the click (as well as .bind) function is only being bound to existing objects and not any new ones. You'll also need (maybe?) to separate the data from the function or you will always add new span tags on each click.
http://docs.jquery.com/Events/live#typefn