I'll try and explain the best I can...
I have a page, we'll call it pageA. When a button is clicked on that page, an ajax request is sent out and the return data is eval()'d. That data obviously contains javascript code. This code could possibly contain event listeners that are added to pageA when it is eval'd. Something like jquery $('body').click() for example.
When the button on pageA is clicked again, is there a function or something to remove any and all event listeners and data added on that eval.
I am capturing all the data returned from the ajax call into a variable before I eval the data, so is there a way to just do $(data).remove() and have all the event listeners and data magically disappear?
With eval data, it's not really added to the page per se where I could just do a text remove on the page and have all my problems go away, but the event listeners certainly do stay around.
Any ideas?
I will forgo the lecture on the evils of eval and recommend that you try namespacing your events in jQuery.
http://docs.jquery.com/Namespaced_Events
For example, have can the code in data add all new events with a namespace:
$(node).bind("click.myNameSpace", fn);
And when you want to remove it, you just do $(node).unbind("click.myNameSpace"), of course, that's assuming you know what elements they were added to. If you don't know the type of event that was added you can also do $(node).unbind(".myNameSpace") and it will remove all events under your namespace.
It may be easier to just copy all the currently attached event handlers on the particular button before you eval the javascript that's returned. Then unbind all the events (to which eval could have added extra handlers) and re-bind what was there before.
EDIT: Looks like someone's already had a similar question: jQuery: Unbind event handlers to bind them again later (though that uses jQuery).
I can't help but ask though, don't you have control over the JS that gets returned? Because if you do, it would be better to make sure malicious content is never sent in the first place.
Related
I have a bunch of jQuery functions that use the .on event because I want to prevent reapplying the event to the same element.
However some people created plugins (e.g. Owl Carousel) and I don't know how to prevent this event from reapplying.
Currently I am using the plugin as following:
HTML:
<div class="init-owl"></div>
JS:
$('.init-owl').owlCarrousel();
$('.init-owl').removeClass('init-owl');
Whenever a second element gets loaded in the page using e.g. AJAX, I want to only apply the event to the newly added element.
Question: What I dont understand is how the event stays stuck to the DOM?
To better grip what is happening, I was wondering how an event in general gets connected to the DOM?
Is there a better way to prevent events applying to the same DOM elements?
If I wish to write my own plugins, I would need to know how javascript works, right?
Question: What I dont understand is how the event stays stuck to the DOM?
Once an event is bound to an object, it gets removed when the object gets garbaged collected. So if a DOM element is really gone and there are no references to it, then the event will get swept up as well.
To better grip what is happening, I was wondering how an event in general gets connected to the DOM?
I'm not sure how far you want to dive into this. Maybe it would help if you stop thinking about the DOM and events and look more at just regular events bound to objects. Basically an object does something, or something is done to it and some underlying code (in the browser's code in this case) triggers an event on that object. The implementations between browsers may differ, but basically you will have a key or string (the event name) that maps to a collection of functions. When you add an event listener, you add another function to this collection. Then when something triggers that event, it iterates through the functions and executes them. That's a real basic explanation, but I hope it makes things a little more clear.
Is there a better way to prevent events applying to the same DOM elements?
Make sure you don't add the events again by writing better code. I don't believe you can dive down into an element and look to see if it has events bound to it. You can however change your jQuery selector to only target newly added elements. If you have to, mark the elements that you have added events to with a class or something. Then you could target your elements by doing $('.init-owl:not(.already-bound)'). There is a better solution to your problem, I can assure you, but we might need more context and code to see a better way to help you.
EDIT:
You can look into jQuery's off() function to remove events. That may help you too.
I'm working with a large template of charts and other widgets. I also manually implemented some ajax tabs. Now whenever those tabs load new content (charts), the problem is that all the template scripts in the head tag won't work with those ajax-loaded elements anymore.
I know, normally you would use .live for this kind of problem, but this would mean to go into the whole 50k lines-js template and change everything to .live calls... Not really able to do that.
Is there instead a jquery way of reloading/reactivating all the scripts within the head-tag?
First off .live() has long since been deprecated and even removed from the latest versions of jQuery. You should never be thinking of using .live().
Second, as it sounds like you already know, the "right" way to fix this is to change your code to use the delegated form of .on() which is what replaced .live(). Yes, change all the code that does it the wrong way. Here's a post on using the delegated form of .on() instead of .live().
Third, a work-around would be to put all your initialization code that hooks up these event handlers into a single function (or called by a single function). Then, you call that single function upon initialization and then you can call that single function any time later after you reload your content. The trick is that you can only put code into that initialization function that can be called or should be called more than once after you content has been reloaded. If you put some event handlers in there that should not be in there, then you may get duplicate event handlers installed. So, only event handler initialization that applies to the replaced content should go in this function.
Suppose that function was called initDynamicContent, then it could look like this:
// init event handlers on the original version of the dynamic content
$(document).ready(initDynamicContent);
Then, sometime later after you replace the dynamic content, you can just do:
// code here that replaces the dynamic content with new content
initDynamicContent();
There is no magic jQuery way for this to happen automatically. jQuery has absolutely no way of knowing which code should be run again and which code should not.
I am working on a Greasemonkey script that will actually upgrade the version of jQuery used on the page. To do this, I need to add a "ready" event handler that will fire after all the other ones that might be on the page.
I know that jQuery waits for the DOM to be manipulable before invoking the ready event handlers, so is there a way to influence the order in which it executes them? Thank you,
They are called in the order they are registered. So from the top of the page to the bottom. If you need this to be the last registered ready callback register it at the very end of the body tag. Also use the $(window).load as opposed to $(document).ready.
The ready handlers are added to a readyList Array, which I'm pretty sure is private, so I don't think you'll be able to influence it directly.
One thing you could perhaps do is add your code to the readyList, but place it in a setTimeout() so it waits a bit to execute. Hopefully all the others will be done first.
Still, you may have troubles when upgrading jQuery like this. For example, there may be differences in the implementation of jQuery.cache which stores event handlers, and other data.
So if jQuery.cache was populated with one version, it may not be compatible with another.
How to control the order of functions being called in jQuery $(document).ready
According to answers given to the question above, they should fire in the order they are added (the ajax-calls in that specific question add more mud to the water than in your question).
When I'm writing some JavaScript I have a set of interface buttons that have their events assigned when the page is loaded. My problem is anything created dynamically wont receive these events.
For example, I'm making a URL checker, whose job is to make sure that any link that goes to another domain brings up an exit interface letting the user know they are leaving. Any links created after the page is loaded, post ajax (no pun intended) or whatever wont have that event naturally as those that existed when the page loaded.
In practice, what's the best way to ensure any newly created items get these sorts of global events?
I like to use jQuery, but this is really a conceptual question.
Should I create a function to re-apply any global link effects, or is there a smarter way besides doing it piecemeal?
If using jQuery, you can use the .live() method.
Normally when binding an event handler, the event handler is bound to a specific set of elements. Elements added in the future do not receive the event handler unless it is re-bound.
jQuery's .live() method works around this by binding its own special event handler to the root of the DOM tree (relying on event bubbling). When you click on an element, if it has no event handler directly attached, the event bubbles up the DOM tree. jQuery's special event handler catches the event, looks at its target and executes any user-specified event handlers that were assigned to the target through .live().
Look into jQuery's live function. It will allow you to attach to events when control are created during load, and whenever new ones are created. There is a performance penalty, but it is not significant unless you are loading a lot of elements.
You can use the .live() jQuery method to add listeners to elements that are created after the page is finished loading. Using your example of the exit link (if I understand it correctly):
$(function(){
$('a.exitLink').live('click',function(event){ /* do stuff when a link of class exitLink is clicked */);
});
This will respond to the click event on any link of class exitLink, regardless of when it was created (before or after onload fires).
Hope this helps :)
Yes put simply, where you might have had this before:
$('selector').click(function () {});
Replace it with:
$('selector').live('click', function() {});
I'm using jQuery in an app which registers user clicks to perform a given action through the .click() binding, and I want this functionality to be available only through a user mousedown. One of my friends pointed out today that it's possible to run $(element).click() from a javascript terminal in Firebug (or something similar), and achieve the same functionality as clicking on the element -- something I'd like to prevent. Any ideas on how to go about doing this? Thanks for your input.
Short answer: No, you can't really prevent it.
Long answer: Any event like a click event is bound to such called Event handlers. Those handlers are functions which are executed when a given event occurs. So if you click on an element, your browser checks if any event handlers are bound to it, if so it fires them. If not, the browser will try to bubble up the event to the parent elements, again checks if there are any event handlers bound for that kind of event .. and so forth.
jQuerys .trigger() method (which is what you actually call if calling .click() for instance) just does the same thing. It calls the event handlers which are bound to a specific element, for a specific event.
EDIT
There might some simple ways to somekind of soft detect a real click, for instance you might check for the toElement property within an event object. That property is not set when triggered. But then again, you can easily fake that aswell with .trigger(). Example:
$(document).ready(function() {
$('#invalid2').bind('click', function(e){
alert('click\nevent.target: ' + e.toElement.id);
console.log(e);
});
$('#invalid1').bind('click', function(){
$('#invalid2').trigger({
type: 'click',
toElement: {id: 'Fake'}
});
});
});
Working example: http://www.jsfiddle.net/v4wkv/1/
If you would just call $('#invalid2').trigger('click') the toElement property would not be there and therefore fail. But as you can see, you can add like anything into the event object.
What are you trying to prevent? Someone messing with your client side script? You can do things like obfuscate your code but not much other than that. But even doing this is just making it more hassle than it's worth in my opinion.
If you don't want people doing certain things move the functionality to the server.
Sorry to be bearer of bad news.
You cannot really do anything against it, it would also be possible to write the complete function and then fire it.
But why is this a problem? If somebody is changing something client side it only affects him. Or are you trying to check some data? This MUST always be done in the backend, because you can never be sure what is really sent to it.
You can check event object (which is passed as first argument to handler) originalEvent.
It will be undefined if event is simulated by .click()
But it's completely useless. You cannot use javascript for security - client has full control over it (and firebug console is just most obvious tool). Client-side security checks should be only hint for user and protection against errors, malicious input can be stopped on server-side only.