I've just noticed something odd with Ajax and Drupal 7. It seems each javascript events is fired multiple times after Ajax has been used on a page.
I've tested it with a clean install and the examples module, and here are the steps to replicate it:
- clean minimum install of Drupal 7.7
- download and enable ajax_example module
- add the following javascript in the "simplest example" page:
$('div').click(function() {
alert('clicked on '+$(this).attr('id'));
});
- navigate to "examples/ajax_example/simplest"
- click anywhere on the page: you will see one event being fired for each div tag
- then try selecting one of the options in the example. This will trigger the ajax stuff.
- after that, try clicking anywhere on the page: at least 3 or 4 events are fired for each div!
I've tried it with many several events and browser and everything is fired multiple times after an Ajax action.
Is this a bug or am I perhaps missing something?
Thank you,
Hubert.
Comments
Ah, ok, I suspect it has to
Ah, ok, I suspect it has to do with behaviors. Admitedly I don't quite get how it works, but I've just read that they applied when content is dynamically added. That might explain why these events are fired all over the place after Ajax has refreshed part of the page.
Here is how I added the javascript to the ajax example module:
and then I created test.js
Is there a way to avoid the behavior to be triggered several time? Or something?
Thanks!
One more observation and then
One more observation and then I will stop my monologue:
if I do this ugly hack (as a test):
instead of the following two steps:
Then I don't get the "multiple events firing after Ajax" issue, which suggests it definitely has to do with behaviors.
Found it
Found the solution...
I should have done:
That is, adding the ".once()" jquery function to make sure the stuff happens only once, and not everytime the behavior gets triggered.
Still has to figure out how behaviors actually work :-)
when you using D7 ajax API,
when you using D7 ajax API, it call detachBehaviors then attachBehaviors again automatically
it explain why your behaviors call 2^(click times) because your behaviors have only attach:function () and doesn't have detach:function()
at your situation, you can do something like this:
you can find more information about detach in misc/drupal.js and about drupal ajax in misc/ajax.js
p/s: my english is not good very much, hope you can understand it
To elaborate on your code, it
To elaborate on your code, it should look like this:
Using the $.once() function ensures that handlers are not attached multiple times.
no.once means you can only
no.
once means you can only trigger the event one times.
sometimes, if you trigger the event after an ajax call, you will trigger 2 times and 3 times if 2 ajax calls, etc...
p/s: ah, you bind click() inside once(), i haven't tried that. but i thing this has no more lucks if you don't have detach
If you use $.once() you don't
If you use $.once() you don't need to detach the handler. With your method, the handlers are detached and reattached everytime, which adds a lot of overhead.
really! thanks.seem you have
really! thanks.
seem you have a lot of experience about Drupal ajax and views.
can you help me at this post: https://drupal.org/node/2018527
plz..
Sorry, I rarely use Views and
Sorry, I rarely use Views and cannot help you with that.
i think views is the most
i think views is the most useful module in Drupal
why you don't use it
Views is essentially an SQL
Views is essentially an SQL query builder, and is a great module for those who want/need it, but I write my SQL queries by hand, so I don't need it.
ah ha, you're right. i didn't
ah ha, you're right. i didn't thing there're someones write their query and ignore views. maybe this is another way for my situation
want the click event again
Hi,
Even I am facing this issue, but unbinding it or detaching it doesnt seem to be a solution for my scenario.
This is because, I may need to click on the button(or any trigger) again at some other time, then will "behaviours" come to know that it has to attach the event again and show the alert once?
Thanks!
Events should never be added
Events should never be added to an element more than one time. If you attach the event multiple times, it will fire as many times as it has been attached. This is why you use $.once().
Hi ,
Hi ,
Thanks for the reply. I am not adding the event multiple times but want it to fire at different times. Let me explain you a little.
I have a div tag and say a button. On click of button, ajax fires and replaces the div tag with html fieldset tag.
Now this fieldset has 2 buttons in it(say Add and Delete). Now jquery comes into picture. When I click on Add button, I want another fieldset tag to be added, but the event get fired twice and 2 fieldset tags gets added. Again when clicked on Add button, instead of adding 1 fieldset tag, it adds 2 again. So now, total fieldset tags should be 3 but they are 5.
I have tried using jquery unbind, it partially solves my problem, when clicked Add button for the first time, it adds only 1 fieldset tag which is correct, but when I again click on it, it doesnt do anything.
So, I think even .once() will work it in same way. From what I have read about it, it will work on initial loaded elements. Do you think it will work on dynamically added elements from ajax response?
Thanks!!
It doesn't matter how the
It doesn't matter how the element is loaded, .once() will work on whatever element it is applied to.
You should probably show your code.
@Jaypan
@Jaypan
even with once() and detach , ajax call is getting multiplied each time. You can check it at http://198.58.126.77/elec/des-candidates by clicking on the text below image.
js file
http://198.58.126.77/elec/sites/all/themes/simplicity/js/cust.js?ox0aae
I think you may be using $
I think you may be using $.once() incorrectly.
This:
Should be this:
tried that too, still not
tried that too, still not working.
could d3js code is the problem? because that element is part of d3js svg and I dont see any *processed class is added to that element.
also tried http://codekarate.com/blog/drupal-7-prevent-duplicating-javascript-behav... 's comment suggestion
but so success.
I don't know what d3js is,
I don't know what d3js is, nor do I know why you have the context==document included there. Also, you have Drupal.behaviors twice in the file. JS should only have one Drupal.behaviors declaration per file.
Strip it down to its bare base until you have something working, then build it up. Right now you've got too much in there adding too many variables to debug your issue.
why you have the context=
"
thats because at http://codekarate.com/blog/drupal-7-prevent-duplicating-javascript-behav... its mentioned as one of the solution for this particular issue. Few lines from that site by Pierre Buyle :
"So if you want to balance stability and performance, always uses context AND jQuery.once(). Not just one of them."
there are none I think and I assume you means
Drupal.attachBehaviors();
. When that line is commented ,ajax is working fine and not getting multiplied in subsequent clicks. There isn't any official doc which mentions when should/avoid usingDrupal.attachBehaviors();
, but some of the code at SO is having this line but without any internal info.Thanks Jaypan for the help on this issue.
thats because at http:/
I actually have to disagree with him.
Using context == document
will mask the issue you are facing, in that context only equals document on initial page load, and therefore code inside it will only ever be executed a single time. If the element you are attaching listeners to exists in the DOM on page load, the listener will be attached. But Drupal's behaviors framework is meant to be used to attach JS not just on page loads, but also on AJAX loads. On an AJAX load, context will not equal document, and therefore listeners will not be attached to any values that were inserted into the DOM using AJAX. The solution he proposes is a band-aid solution to a mistake that has been made somewhere else. It does not fix the initial problem, and will lead to problems if you start working with AJAX on your system.The file you linked to has changed. Now there is only Drupal.behaviors.listload, but previously there was another Drupal.behaviors declaration in the file as well.
Looking at your code as it stands now, it will not attach the event listener more than once. If you are still seeing this, it means one of two things:
1) You are seeing cached code, whether it be a Drupal cache, or a browser cache, or some other cache. Or:
2) You are attaching more than one bit of JS that is attaching the event listener. Maybe you have similar code in another file.
You may want to read this for a better understanding of D7's JS framework: https://www.jaypan.com/tutorial/high-performance-javascript-using-drupal...
All about context!
I haven't found any info in the docs, if anyone does, a link would be welcome :) In any case, try adding context as a parameter to your jQuery object, this should prevent the object being added multiple times after an Ajax call.
The file you linked to has
I had saved old js as cust.js.old which had messy commented code which looks like multiple behavior sections. I cleaned it and tidy it for readability and saved as active js file.
After reading the your link https://www.jaypan.com/tutorial/high-performance-javascript-using-drupal... about attachBehavior like
Should it be mandatory to have Drupal.attachBehavior even though click event element .ajax-button is not a part of ajaxly loaded content (see image for info)?.
Even if we have Drupal.attachBehavior without any context then isn't once() function should take care of parsing *-processed class on sunsequent ajax call which triggers Drupal.attachBehavior?. But Once() function is not adding such class like .ajax-button-processed on page load when it supposed to. am I right here?
image:( attached from #2459953: files placeholder for drupal.org forum posts)
Should it be mandatory to
Drupal.attachBehaviors() is to attach new even listeners to ajax loaded content. Therefore the element that is clicked is irrelevant, it's the newly inserted code that matters. Due to the hook events in Drupal core, it should generally not be assumed that what is inserted into the DOM will not have content that needs JS listeners applied. Therefore, any time new content is inserted into the DOM, that content should be passed through Drupal.attachBehaviors() after it has been inserted.
So the fact that the link that is clicked to trigger the AJAX load is not part of the newly loaded content, is irrelevant to whether or not Drupal.attachBehaviors() should be called.
You should never be calling Drupal.attachBehaviors() without passing it any context. That only happens on page load, and the whole DOM gets parsed. When you call Drupal.attachBehaviors() yourself, you should always pass it the (parent of) the new content, so that only that content is parsed for new HTML that requires event listeners to be attached.
I am almost near to end this
I am almost near to end this bug and found that original issue is with d3js collapse event which collapses the child elements but won't load child elements by ajax, instead it is loading saved data inside Drupal.settings which is available at client side as CDATA.
So my final query is (lets assume it could be anything not just d3js as you are not familiar with it)
how to forcefully trigger Drupal.attachBehaviors when elements are re-added/re-visibled?
or
Isn't browser's job to remember the event attached to element and re-attach when elements are added again to DOM?(just a thought)
How about a FLAG ?
how about using a FLAG as shown below?
Here is the custom FLAG i am using to prevent multiple calls
That will work, but the
That will work, but the jQuery $.once() method is the Drupal way of doing it.
attaching the gif of whats
attaching the gif of whats happening
Code to add new elements
Hi,
Below is the function attached on click of 'ADD' button
I already have one fieldset created on my page.
Issue is, when I click on 'ADD' button on my page, this event get triggered and a new fieldset gets added, which is perfectly as I want it to behave.
But, in this newly added fieldset, I have 'ADD' button again, and on click of this 2nd 'ADD' button, I want a new fieldset to get added, which doesn't happen.
Could you please help me in this?
Thanks!