I'm using a table for a coordinate system, and with a function in each td element to update the field value.
[ ][ ][X][ ]
[ ][X][X][X]
[X][ ][ ][ ]
When I click an empty element, I want it to set the value to "X" in my database, by getting new partial data from my rails app (and sending GET-variables along), and then re-render the table. This works just fine using my rails app, however ONLY once. It seems like the jQuery function .click() only runs once. The function looks as following: (generated via. coffeescript)
$(document).ready(function() {
$(".field").click(function(e) {
$.get("http://localhost/dinners?ap="+$(this).attr('id'), function(data) {
$("#dinner_table").html(data);
});
});
});
This will run just fine, and once I click an element, it will render new data in the table - but only once! If I redefine the .click() function after I swap the HTML in the function, it allows me to click (and update) twice, etc.
I'm pretty sure this is a basic question, but no luck with the searches so far.
Delegate the event. By doing this you need attach the handler only once.
$(".field").click(function(e) {
Will become:
$("body").on('click', '.field', function(e) {
body can be replaced with any static parent container.
Use event delegation:
$( '#dinner_table' ).on( 'click', '.field', function () {
/* your click handler */
});
So, the click-handler is bound on the common ancestor (the TABLE element, in this case).
Related
I have a jQuery dynamically created table that appends data from json file.
one of the rows of the table is a row of buttons that are appended into a row variable that is appended into the table:
var like = $("<a href='index.html'><button class='likeBtn'>like</button></a>");
var comment = $("<a href='index.html'><button class='comBtn'>comment</button></a>");
var toggle = $("<a href='index.html'><button class='togBtn'>show/hide comments</button></a>");
row3.append(like).buttonset();
row3.append(comment).buttonset();
row3.append(toggle).buttonset();
$("#table").append(row3);
now I need to toggle the row below in the table when clicking the toggle button.
this is my onclick function:
$(function(){
alert("in");
$('.togBtn').click(function() {
alert("in2");
$(this).closest('tr').toggle();
});
});
when I put alerts inside the click function I don't see them, I do see alerts from the function that holds the click function. for example- I see "in" but I don't see "in2".
and of course the row is not toggled.
commentRow is the class of the row that needs to be toggled.
I tried lots of options like-
$("#table").closest('.commentRow').toggle();
also with next() , All(), and many others and I can't get it to work!!!
please - your thoughts on this.
All help will be much appreciated!
It's due to the dynamically generated content, try that:
$(document).on('click','.togBtn',function(e) {
alert("in2");
$(this).closest('tr').toggle();
// or return false; // it does both preventDefault & stopPropagation.
});
This is called event delegation. This technique is only used when you have generated dynamic DOM nodes like as you are doing in your code.
So, in this case all the events were bound when page was initially loaded and the elements are generated after page load, due to that browser didn't registered any event for those elements because of unavailablity. In this case event has to be delegated to the static parent node or to the document itself because it is always available.
Syntax for event delegation using .on() method:
$(staticParent).on(event, selector, cb);
With the help of the answers posted here I found a solution that works:
$(document).on('click','.togBtn',function(e) {
alert("in2");
e.preventDefault();
$(this).parents("tr").next().slideToggle();
// or return false; // it does both preventDefault & stopPropagation.
});
Thanks all for your help!
I am trying to write some code for change() event using jQuery Text Editor (jqte), I have two functions which give jqte functionality to textarea's
One for editors loaded with JavaScript, when clicking some elements in a page:
function onLoadEditor(){
jQuery(".comment-editor").jqte({
// some jqte params, such as fsize: false,indent: false...
change: function(){ observeEditor(); }
});
}
And other, generic function, for pages with one single editor
jQuery(function() {
jQuery(".comment-editor").jqte({
// some jqte params, such as fsize: false,indent: false...
change: function(){ observeEditor(); }
});
});
I want to access the id of the concrete textarea (all textareas in the page have an id) which has fired the change() event
How should I write observeEditor() function to achieve this? Or... how I should define the function in jqte change property?
After reading this jQuery blur event with ID and value I have solved it, with following code (simplified)
function onLoadEditor(){
jQuery(".comment-editor").each(function(idx, elem) {
jQuery(this).jqte({
// some jqte params, such as fsize: false,indent: false...
change: observeEditor(elem.id),
});
}
jQuery(function() {
onLoadEditor();
});
But now I have another problem...
As you can read in the original question, onLoadEditor() is called when clicking some elements in a page. Then another javascript function jsComment() is called, builds a form (with a textarea.comment-editor field included) and it is rendered this way
function jsComment(){
...
var form = '<div class="comments_wrapper ... ';
jQuery(form).insertAfter(some_element).fadeIn('fast');
onLoadEditor();
}
Problem is change() event is being fired only once, when form fades in, while the idea is the opposite, event should fire when user adds some text, not when appearing... Any tips?
UPDATE
After reading Event binding on dynamically created elements? I have solved it this way
function onLoadEditor(){
jQuery('.comment-editor').each(function(idx, elem) {
jQuery(this).jqte({
// some jqte params, such as fsize: false,indent: false...
});
jQuery(document).on('change',
jQuery('.comment-editor'),
function(){
observeEditor(elem.id);
}
);
});
}
jQuery(function() {
onLoadEditor();
});
Although finally I am not using change() event, as it was being fired constantly. Performing better with keyup() & paste(), for instance
I am trying to make ajax call on focus for every text input, I can make is in first page( when document ready) but when I change page , javascript can't parse inputs because it wasn't created when document is ready. How can I fix it ?
jQuery(document).ready( function(){
jQuery('[id^=urun_sirasi-]').focus(function(){
event.preventDefault();
var urun_sirasi=jQuery(this).data('sira');
console.log(urun_sirasi);
jQuery('#urun_sirasi-'+urun_sirasi).bind('keyup',function(e)
{
console.log(jQuery("#urun_sirasi-"+urun_sirasi).val());
jQuery.ajax({
url:'../ajax.php',
data:'process=siralama&urun_id='+urun_sirasi+'&urun_sirasi='+jQuery.trim(jQuery("#urun_sirasi-"+urun_sirasi).val()),
success:function(e){
// e -> 1 ve ya0 geliyor.
console.log(e);
}
});
});
});
});
Thanks for your help.
This seems to a very common problem in the jQuery section. See the docs for .on() at http://api.jquery.com/on/, specifically the section about delegated events:
Delegated events have the advantage that they can process events from
descendant elements that are added to the document at a later time.
I use the following syntax in the doc ready that will create events for all future items with the "expand" class, should be able to be adjusted for you
$(document).on('click',"#myTable .expand", function(){
so yours should be something like this (maybe give your items a class rather than having an event to each element selector)
$(document).on('keyup',"#yourTable .urun_sirasi-key", function(){
I am using JQuery to add a row to a table. Within the row is an element who's click event I am capturing:
$(document).ready(function() {
var newRow = "<tr> ... </tr>";
$('tableName > tbody:last').append(newRow);
$(...).click( function() { // click event on newly inserted elem in the row
alert("click");
});
}
This works great and the alert comes up as expected. BUT when I now add the new row dynamically via an ajax call and parsing the xml, it stops working:
$(document).ready(function() {
getData();
$(...).click( function() { // click event on newly inserted elem in the row
alert("click");
});
function getData() {
$.ajax({
...
success: function(data) {
//parse the xml
$('tableName > tbody:last').append(parsedXml);
}
});
}
The html is correctly added to the DOM, but the click event is no longer captured. Is there some issue with scope or closures going on here?
use
$(...).live('click', function() { // click event on newly inserted elem in the row
alert("click");
});
This keeps the click event running after it has been used
more info
When working with a table, I like to use .delegate() for this. It's very similar to .live(), but lets you set up an event listener on a single parent element, rather than individual handlers on every child element. So whether you have a table of one row or 1000, you still need only one handler.
$('#yourtable').delegate('your_descendant_element','click', function(){
alert("click");
});
You should look into using the live() event handler. It allows you to create an event that matches elements created in the future dynamically, which is what is not happening right now. Another way to fix it would be to move the all to bind down below where you append the new table row, but live() is a much better answer.
I have two divs, one that holds some stuff and the other with all possible stuff. Clicking on one of the divs will transfer items to the other div. The code I came up with is:
$("#holder > *").each(function() {
$(this).click(function(e) {
$(this).remove();
$("#bucket").append(this);
});
});
$("#bucket > *").each(function() {
$(this).click(function(e) {
$(this).remove();
$("#holder").append(this);
});
});
This one works perfectly, except that the event handlers need to be refreshed once I append or remove elements. What I mean is, if I first click on an element, it gets added to the other div, but if I click on this element again, nothing happens. I can do this manually but is there a better way to achieve this?
Try jquery live events .. the $.live(eventname, function) will bind to any current elements that match as well as elements added to the Dom in the future by javascript manipulation.
example:
$("#holder > *").live("click", function(e) {
$(this).remove();
$("#bucket").append(this);
});
$("#bucket > *").live("click", function(e) {
$(this).remove();
$("#holder").append(this);
});
Important:
Note that $.live has since been stripped from jQuery (1.9 onwards) and that you should instead use $.on.
I suggest that you refer to this answer for an updated example.
First, live is deprecated. Second, refreshing isn't what you want. You just need to attach the click handler to the right source, in this case: the document.
When you do
$(document).on('click', <id or class of element>, <function>);
the click handler is attached to the document. When the page is loaded, the click handler is attached to a specific instance of an element. When the page is reloaded, that specific instance is gone so the handler isn't going to register any clicks. But the page remains so attach the click handler to the document. Simple and easy.
Here you go, using the more intuitive delegate API:
var holder = $('#holder'),
bucket = $('#bucket');
holder.delegate('*', 'click', function(e) {
$(this).remove();
bucket.append(this);
});
bucket.delegate('*', 'click', function(e) {
$(this).remove();
holder.append(this);
});
EDIT: don't use live, it be deprecated!
Take advantage of the fact that events bubble. Using .on():
var = function( el1, el2 ) {
var things = $('#holder, #bucket');
things.each(function( index ) {
// for every click on or in this element
things.eq(index).on('click', '> *', function() {
// append will remove the element
// Number( !0 ) => 1, Number( !1 ) => 0
things.eq( Number(!index) ).append( this );
});
});
any click on any element (existing at the time of bind or not) will bubble up (assuming you haven't manually captured the event and stopped propagation). Thus, you can use that event delegation to bind only two events, one on each container. Every click that passed the selector test of the 2nd argument (in this case, > *, will remove that element and then append it to the alternate container as accesesed by things.eq( Number(!index) )
Have you looked at jQuery's live function?
The most Efficient way (dont load all event for all elements) it:
//NORMAL FUNCTION
function myfunction_click(){
//custom action
}
$('id_or_class_of_element').on('click', myfunction_click);
//LOAD OR REFRESH EVENT
$(document).on('click', 'id_or_class_of_element', myfunction_click);