what happens to window events when changing documents? - javascript

Hey i have the following statechange event set on the window :
History.Adapter.bind(window,'statechange',function(e){
console.log("statechange event occured ");
//more code
var newDoc = document.open();
newDoc.write(file);
newDoc.close();
});
i'm using history.js but that doesn't matter in this case as it binds the statechange regulary, i am getting the value of file like should be and works fine.
Now i have this code (and other code) inside an external js file,
inside the file i'm iterating through all a tags and apply the following click event :
$(element).on("click",function(e){
e.preventDefault();
//more code
History.pushState({file : file},title, fullHref);
});
Now when i click the document is getting changed as expected but when trying to use back/forward buttons statechange event does not fire.
I should mention that this js is included in the files im loading as well.
So my initial thought was that as the document changes but the window doesn't, the event remains. which is not true as it applies the statechange event multipile times. So i tried applying the event once using cookie but that still does the same thing.
Now if instead of changing the document i simply apply it with jQuerys .html() the statechange event gets fired as exepected so i guess it's related to the document.
Why could this happen? I believe if i understood more about what happens to window events when changing documents i could solve this problem.
Tries :
I have now also tried binding to the popstate event via regular History API, still same results give or take. pushState doesn't fire popstate but back button does but without state.
I have come to the conclusion that i shouldn't be using history.js as i susspect the problem came from there so i "solved" my problems with popstate which still causing problems as document.open seems to be firing popstate. still looking for more information
I've tried using on, same result
Information :
the fact this example us using History is because of history.js, if i use regular history API still same problems.
I have read in mdn that :
{{ gecko_minversion_note("1.9.2", "Starting with Gecko 1.9.2, document.open() uses the
principal of the document whose URI it uses, instead of fetching the
principal off the stack. As a result, you can no longer call
document.write() into an untrusted document from chrome, even using
wrappedJSObject.") }}
Which im not sure but i think could be realted to this problem

Well, in the end i discovered that all though you only change document, window events gets deleted as they lay inside the document.
I encoutred my problems with History.js i think because the fact that im changing documents break it at some point.
What i did was use native HTML5 history API and rebinded the popstate event in the new document.

have you tried to bind the element with LIVE ?
$(element).click(function(e){
e.preventDefault();
//more code
History.pushState({file : file},title, fullHref);
});
becomes
$(element).live('click',function(e){
e.preventDefault();
//more code
History.pushState({file : file},title, fullHref);
});

You have to use iframe to load your document and easily can change its document. Then, you can listen for a click event like this,
$("#iframe").load(function () {
$(this.contentWindow).click(function () {
// do your click events here
});
});
The iframe will reload if you change the src attribute value as another document.

Have you tried setting a target in the document.open() method (see
http://www.w3schools.com/jsref/met_doc_open.asp)?
If not' you're actually writing to a blank window, where you might be losing the History object.

Related

Can you destroy the document element and event listeners without using document.write()?

In the example below:
If you right click the document, it will tell you it's listening.
If you click m1 it will replace the document element, but right clicking the document will still inform you that it's listening. You must right click near the top because the document has no contents.
If you click m2 it will overwrite the document contents and right clicking near the top no-longer does anything. Examining the document in the development tools verifies that the event handlers are gone.
After pressing one button, you must "run code snippet" again to try the next because this demonstration is destructive.
With this information. Is there a different way to destroy the document and replace it with a new one, in such a way that the event handlers are destroyed, without using the document.write() function?
document.write() is prone to errors and its usage is "strongly discouraged", but I would still like to be able to destroy the document and it's event listeners.
document.addEventListener('contextmenu', function (e) {
alert('still listening');
e.preventDefault();
})
function m1() {
var doc = document.implementation.createHTMLDocument();
document.replaceChild(
document.importNode(doc.documentElement, true),
document.documentElement
);
}
function m2() {
document.close();
document.write('<html></html>');
}
<button onclick="m1()">m1</button>
<button onclick="m2()">m2</button>
To be clear, button/function "m1" fails my goals, because although the document element was replaced, the event handlers from the previous document element were preserved for some reason. I would like to achieve what m2 achieves, but without using document.write and document.close which are recommended against.
Addendum:
This question is strictly for the sake of better understanding the limits of the language and the engines that implement them. Please do not try to read between the lines or solve some alternate goal. It's just a question of whether something is possible or not.
I don't need to know how to remove event listeners or manage event listeners, or remove all child elements of a document. I would like to know if it's possible to destroy the document element itself, leaving no <html> tag whatsoever and leaving none of its event listeners behind. This is possible with document.write() but I simply want to know if there are alternate means of achieving this exact goal, not any other assumed goals.
I just realised that actually you can, and that the first example, m1, actually does replace the documentElement and removes its event listeners. The interesting thing is that there were no event listeners on it in the first place, they were on the document itself not the document element. The developer tools (in FireFox) tricks you and shows them as being attached to the <html> element, even though they aren't technically attached to an element. If you modify the example to actually attach the events to the document.documentElement instead of the document itself the event will be disabled when you click m1:
document.documentElement.addEventListener('contextmenu', function (e) {
alert('still listening');
e.preventDefault();
})
function m1() {
var doc = document.implementation.createHTMLDocument();
document.replaceChild(
document.importNode(doc.documentElement, true),
document.documentElement
);
}
function m2() {
document.close();
document.write('<html></html>');
}
<button onclick="m1()">m1</button>
<button onclick="m2()">m2</button>
Originally I was going to say no, and that you can't do it, as user Livingston Samuel states in this similar but not identical question: "You cannot replace the current document object or any document object with the Document object created with createHTMLDocument method."
Sticking to my original question though, it is not the document I was asking about, it was the documentElement. So given the modified code above, I will say that Yes, it is possible.
It is interesting to see that the event listeners are displayed on the html element in FireFox whether they are attached to the document object or the documentElement object, as of version 81. I might expect that they would instead not display them at all when attached to document, or put them on some abstract object. An iframe does have an abstract object available labeled #document, but it is not used for this and has no event label:
It is also interesting that document.write() actually destroys the events on the document object, not just the document.documentElement. It might be documented somewhere in here but I can't seem to find it.
After testing Chrome I noticed that the developer tools distinguishes between events being attached to the documentElement:
and the document itself:
I'm not sure what's behind this question but I may guess, that you are having issues when the objects and events are re-created in the page.
If this is the case, as can be seen in MDN (https://developer.mozilla.org/en-US/docs/Web/API/Node/removeChild), despite you remove the elements on the HTML, they still could remain in the memory for a while, waiting for the garbage collector to clean them up.
So in case you wish to remove safely a piece of code that contains events, the safest way is to remove the events first using your favorite library (like jQuery) or using the DOM through native JavaScript:
https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/removeEventListener
Once you have removed all events, the you could remove all elements, and the safest way to do this (from a memory point of view) is through a loop, removing the inner elements first and the parent elements then, and so on.
You can use document.getElementById('id-here').innerHTML = "", like so:
<html id="the-website">
<body>
<input type="button" onclick="document.getElementById('the-website').innerHTML = '';" value="Don't click me plz!">
</body>
</html>

Click on Gmail's button using JS or jQuery

I'm trying to click on "close" button in Gmail compose dialog using JS from chrome console. This button have a class "Ha". Classes are static and will not be changed after the refresh.
Screenshot
I've tried to use this code, but nothing happens:
document.querySelector(".Ha").dispatchEvent(new MouseEvent("mousedown"));
Here is the solution:
var test = document.getElementsByClassName("Ha")[0];
var e = document.createEvent('Event');
e.initEvent("mouseup", true, true);
test.dispatchEvent(e);
To answer the basic jQuery issue what you need is...
$( ".Ha" ).trigger( "click" );
to trigger a click event on the dom object in question. While when you click on an object using the mouse a mousedown, a mouseup, and a click event are all all triggered, by triggering through code you need to specify which specific event is triggered base on which event is monitored for by the web app. You also need to be careful about using class based selectors as classes can exist in more than one place in the dom structure and you could wind up triggering click events on the wrong dom object or on multiple dom objects which can cause unexpected results, the best way is to use an object's id tag if it exists.
As a side note be careful with the use case for what you are trying to do as you could be violating Google's terms of use by writing code to go around their UI and their own coding.

How can I see the event that is attached to an html element?

I am a new programmer and still learning.
This is the code that I am trying to figure out:
<div id="buy" class="buy button">Buy</div>
When I click on the div (button), some JavaScript code is executed but I don't know were it is. How can I tell what function is fired when click happens? Some how a listener is attached to this element.
In Google chrome's developer tools (click the wrench icon >Tools>Developer tools), select the element in the Elements tab, on the right open the 'Event Listeners' panel you'll will see all events
If you use Firefox and Firebug you can try installing FireQuery. It will make it so you can see the handlers bound by jQuery. http://firequery.binaryage.com/
You can't do it in a really good manner by "just" using ECMAscript itself. For instance, if there was a click event handler added by DOM Level 1 in the form of
document.getElementById('buy').onclick = function() {};
you can of course easily intercept that property on the node itself. Things are getting more complicated if DOM Level 2 comes into play with .addEventListener() respectevily .attachEvent(). Now you don't really have a "place" to look for where all the different listener functions where bound from.
It gets better by using jQuery. jQuery will hold all it's event handler functions in a special object which is linked to the DOM node of invocation. You can check for that by getting the .data()-expando property for a node like
$('#buy').data('events');
However, now I already described three different ways of binding event listeners to a node (actually its two because a library like jQuery also uses DOM Level 1 or 2 methods of course).
It's really getting ugly if an event is triggerd by delegation. That means, we bound our click event on some parent-node just waiting for that event bubbling up to us so we can check the target. So now we don't even have a direct relationship between the node and the event listener.
Conclusion here is, lookout of a browser plugin or probably a thing like VisualEvent.
You may use "Visual Event 2" script as a bookmark or same script as Chrome extension.
This script shows all js events attached to dom-elements.
Use jQuery("#buy").data('events');
http://api.jquery.com/jQuery.data/ may be interesting.
Event handlers attached using traditional element.onclick= handler or HTML <element onclick="handler"> can be retrieved trivially from the element.onclick property from script or in-debugger.
Event handlers attached using DOM Level 2 Events addEventListener methods and IE's attachEvent cannot currently be retrieved from script at all. DOM Level 3 once proposed element.eventListenerList to get all listeners, but it is unclear whether this will make it to the final specification. There is no implementation in any browser today.
If you're using FireFox, you should have FireBug installed. Once you have that, you can install FireQuery, which will show you what jQuery events are bound to which objects.
http://getfirebug.com/
http://firequery.binaryage.com/
This is the easiest way I've found of how to do it:
http://www.sprymedia.co.uk/article/Visual+Event
When working with events in Javascript, it is often easy to lose track
of what events are subscribed where. This is particularly true if you
are using a large number of events, which is typical in a modern
interface employing progressive enhancement. Javascript libraries also
add another degree of complexity to listeners from a technical point
of view, while from a developers point of view they of course can make
life much easier! But when things go wrong it can be difficult to
trace down why this might be.
It is due to this I've put together a Javascript bookmarklet called
Visual Event which visually shows the elements on a page that have
events subscribed to them, what those events are and the function that
the event would run when triggered. This is primarily intended to
assist debugging, but it can also be very interesting and informative
to see the subscribed events on other pages.
There's a bookmark button there you can drag to your toolbar (FF or Chrome), then just click the button on any page where you want to see the events attached. Works great! (at least for events attached by jQuery or other libraries).
Are you using jQuery? If so, you want to search for one of these three lines of code:
$("#buy").click //the div is refered by its id
or
$(".buy").click //the div is refered to by the style "buy"
or
$(".button").click //refered to by the style "button"
Most newer browsers have "Developer Tools" built into them by pressing F12 (at least in IE and Chrome). That may help you do some further debugging and tracing.
Below is something I’ve used in the past that I think may be what you're looking for. What this does is watch a property on a page element (In the example below, it's the document's "Title" property) and then display an alert with the JS callstack whenever that property is changed. You’ll need to get this into the DOM before whatever code you're trying to find fires, but hopefully you’ll be able to identify what’s causing the problem.
I would recommend using Firefox and getting Firebug for JavaScript debugging.
// Call stack code
function showCallStack() {
var f=showCallStack,result="Call stack:\n";
while((f=f.caller)!==null) {
var sFunctionName = f.toString().match(/^function (\w+)\(/)
sFunctionName = (sFunctionName) ? sFunctionName[1] : 'anonymous function';
result += sFunctionName;
result += getArguments(f.toString(), f.arguments);
result += "\n";
}
alert(result);
}
function getArguments(sFunction, a) {
var i = sFunction.indexOf(' ');
var ii = sFunction.indexOf('(');
var iii = sFunction.indexOf(')');
var aArgs = sFunction.substr(ii+1, iii-ii-1).split(',')
var sArgs = '';
for(var i=0; i<a.length; i++) {
var q = ('string' == typeof a[i]) ? '"' : '';
sArgs+=((i>0) ? ', ' : '')+(typeof a[i])+' '+aArgs[i]+':'+q+a[i]+q+'';
}
return '('+sArgs+')';
}
var watchTitle = function(id, oldval, newval) { showCallStack(); }
// !! This is all you should need to update, setting it to whatever you want to watch.
document.watch("title", watchTitle);
Right-click page, and choose to view the page's source
Find <script> tags
Look for $("#buy") and something mentioning onClick or .on("click",function(){...});
If you can't find it, search for something along these lines: document.getElementById("buy")
You have found the function, or code, where the event handler code is
$("#buy") is JQuery's way of saying find an element that has an id attribute of buy and if it has a . following it with some function, that function is acting upon the element that was found by JQuery.

Opera ignoring .live() event handler

I have the following jQuery which works in all major browsers except Opera:
jQuery(document).ready(function () {
jQuery("#GetResults").live("click", function(e){
e.preventDefault(); //Opera doesn't execute anything here
});
};
Which is supposed to fire when clicking the following link:
<a id="GetResults" href="Folder/File/javascript:void(0);">Get Results</a>
Only Opera ignores this. Any ideas?
Edit:
I've just discovered that if I substitute out .live() for .bind() everything functions as expected. I can't find any documentation relating to .live() bugs in Opera though, and it does work in jsFiddle which would point at something environmental. What could be causing this behavour?
This needs clarification. The answers above are correct, but nobody clearly explained where your problem comes from.
In fact I think that you could probably reproduce the problem in other browsers too.
That's because of how .live works:
It binds to the event on document and waits for a particular event to bubble up to there. Then it checks if the event.target is what you wanted to handle. *
If you click on a link element it's quite possible that the browser goes to the new page before the event bubbles high enough to trigger your code. In an app with lots of HTML and event handlers all the browsers should have problems. Opera just starts displaying the new page and destroys the previous quicker in this case. It really depends on a particular situation more than on the browser. For example: you probably won't see this happen if you had a high network latency while connecting to the site.
To prevent default action on a a element you have to use .bind like in the old days ;) when a eveloper had to be aware of what he loads with AJAX and bind new events to that in a callback.
* There is more to that and .live is more complicated. I just described what is needed here.
What happens when you attach the handler using:
$ (something).bind ("click", function (e) {
// do something
})
You can also try to attach the handler using .click() method.
The following code works as expected in Opera 11.50.
<!doctype html>
<title></title>
<a id="GetResults" href="http://google.com">Get Results</a>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.6.2/jquery.min.js"></script>
<script>
jQuery(document).ready(function () {
jQuery("#GetResults").live("click", function(e){
alert('doing something');
e.preventDefault(); //Opera doesn't execute anything here
});
});
</script>
Either it is a corrected bug, or something more subtle.
Can you check whether the above works on your version of Opera / jQuery?
Read this article: http://jupiterjs.com/news/why-you-should-never-use-jquery-live
try use delegate instead
Not sure if you want to do it, or if it will work for you. I had similar issues with Opera 9.5 and e.preventDefault() not working, the only solution I found was to just return false...
jQuery(document).ready(function () {
jQuery("#GetResults").live("click", function(e){
e.preventDefault();
return false;
});
};
There are two aspects of an event bubbling worth considering in this case: propagation and the default action.
Propagation refers to the event bubbling. First the anchor tag gets the click event, then its parent element, then its parent's parent, and so forth, up to the document element. You can stop an event from propagating at any time by calling e.stopPropagation().
The default action is what the browser will do if nothing is done to prevent it. The most well-known case is when an anchor with an href is clicked, the browser will try to navigate there. There are other examples too, though, for example when you click and drag an image, many browsers will create a ghost image you can drop on another application. In both cases, you can stop the browser from doing the default action at any time by calling e.preventDefault()
As mentioned in other answers to this question, jQuery's .live() feature sets a handler at a high level element (like document) and takes action after events have propagated up. If a handler in between the anchor and the document calls e.stopPropagaiton() without calling e.preventDefault() it would stop the live handler from responding, while still allowing the browser to navigate (the default action).
I doubt this is what's happening, since it would affect all browsers, but it's one possible explanation.
Ensure that document.ready event happens before you click on link.
Try to put all lives in the top of the document.ready wrapper. It may help, if you have a lot of javascript code.

Locating an element in a 'Facebox' box

Heres my link:
http://tinyurl.com/6j727e
If you click on the link in test.php, it opens in a modal box which is using the jquery 'facebox' script.
I'm trying to act upon a click event in this box, and if you view source of test.php you'll see where I'm trying to loacte the link within the modal box.
$('#facebox .hero-link').click(alert('click!'));
However, it doesn't detect a click and oddly enough the click event runs when the page loads.
The close button DOES however have a click event built in that closes the box, and I suspect my home-grown click event is being prevented somehow, but I can't figure it out.
Can anyone help? Typically its the very last part of a project and its holding me up, as is always the way ;)
First, the reason you're getting the alert on document load is because the #click method takes a function as an argument. Instead, you passed it the return value of alert, which immediately shows the alert dialog and returns null.
The reason the event binding isn't working is because at the time of document load, #facebox .hero-link does not yet exist. I think you have two options that will help you fix this.
Option 1) Bind the click event only after the facebox is revealed. Something like:
$(document).bind('reveal.facebox', function() {
$('#facebox .hero-link').click(function() { alert('click!'); });
});
Option 2) Look into using the jQuery Live Query Plugin
Live Query utilizes the power of jQuery selectors by binding events or firing callbacks for matched elements auto-magically, even after the page has been loaded and the DOM updated.
jQuery Live Query will automatically bind the click event when it recognizes that Facebox modified the DOM. You should then only need to write this:
$('#facebox .hero-link').click(function() { alert('click!'); });
Alternatively use event delegation
This basically hooks events to containers rather than every element and queries the event.target in the container event.
It has multiple benefits in that you reduce the code noise (no need to rebind) it also is easier on browser memory (less events bound in the dom)
Quick example here
jQuery plugin for easy event delegation
P.S event delegation is pencilled to be in the next release (1.3) coming very soon.

Categories

Resources