document.write and delegated event handler persistence - javascript

I'm just testing out replacing a whole page with another page using JavaScript and I found this answer with document.write. As to why document.write, I needed to replace the entire HTML, including scripts and styles using the same page.
It does what I want but i can't seem to have consistency with my event handlers. My handlers are all attached to document using:
$(document).delegate(...);
Currently, I have weird results. In a fiddle I made, it attaches a handler. When clicked, the event fires, rewrites the page, runs the function again - but it doesn't attach the handler.
However in my project, I'm doing the same routine (d.w(), then add handlers). It does reattach once and handlers work, but after doing a second routine (still on the same page), it doesn't attach anymore.
So my questions are:
When using d.w(), do existing handlers get erased from document?
Are window as well as document the same after subsequent d.w()s? or are they somehow "renewed"
Do scripts that are already parsed stay in memory and run after subsequent d.w()s? Or do they get erased as well?

(The following applies to google chrome)
Only the document is cleared, the scripts in memory still stay the same. You can easily test it by setting something to a variable and see if it exists after clearing out the document with .open.
The old native handler is therefore lost from the document, but jQuery still thinks that the handler exists in its own event model. You can see it by editing the log to:
console.log('patch', JSON.stringify($.cache ));
jQuery only ever attaches a single native handler per event, so if you have a "click" event registered on document, further handlers attached with jQuery don't attach a new native handler, instead the handler is pushed into the jQuery internal handlers array.
Now, because document.open removed the native handler, but doesn't clear javascript, jQuery still thinks the native handler exists, and further .delegate only goes to the jQuery internal handler array. If you replace your handler with plain old document.onclick you will see it starts working.
You can also keep using jQuery if you add $(document).unbind() (or more robust $.cache = {};, but this is internal and subject to change) before the .delegate, so that jQuery is again synced. Otherwise it won't be, since it has no idea you called document.open.
So:
Yes
They are still the same objects, can be tested by saving a reference and checking that agaist document after a .open
They stay in memory.
http://jsfiddle.net/wphzt/4/

The only reason it stops working from second time onwards is because in your function you have written
document.write('<span>'+(++i)+'</span>');
In which case, next time the document doesn't have the delegate function to increment the span value but has only what you have written in the code snippet I have highlighted above. Thus, as you doubted, yes they get erased as well. Hope this helps.

Related

Values in Google Tag Manager are undefined but in console are ok

I'm pushing values from website to Facebook through GTM, but I don't have access to make code changes on server side.
So every value that I need to send to Fb I have to find using Custom Javacripts in GTM.
I can get those values in console but I don't know why I can't see them in Debug mode. It's always undefined.
Here is one of the code in GTM:
function() {
var prod = document.getElementById("product_addtocart_form").elements.namedItem("product").value;
return prod;
}
So what am I doing wrong?
Assuming that line of code works in console, I suspect the code in your function is being fired too early and the elements aren't ready yet.
Make sure your code is not fired until the gtm.dom event is fired, if that still doesn't work then wait until gtm.load is fired. If that still doesn't work then have a look at when these elements are actually ready (maybe they're created using an AJAX call?).
If the gtm native events aren't late enough, when you know the elements are ready you should fire a custom event into your dataLayer which triggers your tags and repopulates the variables. Hopefully this will ensure your variables are populated correctly when your tags are fired.
If the elements are loaded using jQuery AJAX you should be able to bind to the ajaxComplete event on the document. See the jQuery docs. You would then fire a custom event into the data layer which would trigger the tags that using the data in the variables that should now be populated. You'd set up a trigger with type=custom event and the value being 'ajax_complete' (or whatever you call your event).
Your code would look something like this:
$(document).on('ajaxComplete', function(){
dataLayer.push({'event':'ajax_complete'});
});
If there's multiple AJAX events being fired on a single page then you'll have to write some logic to differentiate between calls and/or make sure you don't fire it multiple times. There's also a setting in tags in GTM to say 'only fire once per page load' so you could leverage that as well.

SAPUI5: How to bind a click event to horizontal layout?

Hi I'm developing my view in JS and I'm stuck in binding a click handler for my horizontal layout element. I've tried using Jquery
$("#myHorizontalLayout").bind("click",function(){window.alert()});
Which didn't work then I tried using attachPress with the element which obviously didn't exist. Please help.
Update:
The JS view is the default view of the application.
When on/bind does not work, it could be that the HTML of the control has actually not been created yet at this point in time. But even if you delay the binding, the re-rendering (re-creation of the HTML after changes) would remove your listener, at least when bound on the control itself.
A proper way of doing this is using the generic attachBrowserEvent function available on every control (here: on the layout) which internally handles all the rendering/rerendering stuff, see this example:
http://jsbin.com/hijutunefi/1/edit?html,output
attachBrowserEvent works for any browser event, as it attaches a new browser event listener to the root node of the control. For the most common browser events UI5 does event delegation, so for the "click" event and several others addEventDelegate can also be used, as pointed out by aborjinik.
Alternatively, listening on the <body> level with normal jQuery mechanisms should in general also work.
Which didn't work then I tried using attachPress with the element which obviously didn't exist. Please help.
Does this means that the element on which you are attaching event handler doesn't exists at this point? If this is the case you can hook the handler to some container, upper in the DOM hierarchy which you are sure that exists and filter the click events.
Example:
$("body").on("click", "#myHorizontalLayout", function(){
alert("Hey, you!");
});
As of jQuery 1.7, the .on() method is the preferred method for
attaching event handlers to a document. For earlier versions, the
.bind() method is used for attaching an event handler directly to
elements. Handlers are attached to the currently selected elements in
the jQuery object, so those elements must exist at the point the call
to .bind() occurs.
Reference here
So try replacing bind with on and let me know if it works or not.

Can't modify properties of element using jquery .on()

I'm currently trying to write what I feel like should be a very simple chrome addon using jquery. I have a tool I use for work that our IT department has stopped supporting Chrome with, because they have enough on their plate troubleshooting IE. Their solution however, was simply to remove the old onClick functions and added the property disabled="diabled" to all of our buttons.
My simple work around for this is using jquery to remove the disabled properly and append the onClick functionality. I've gotten this to work in a few instances, but the problem I'm running into is with new instances of buttons created using ajax forms.
Here's the code I'm currently trying to work with:
function restoreFunctionality() {
$("#RestoreDefaultsButton").removeProp("disabled").attr("onClick", "OnRestoreDeviceClientClick()");
}
RestoreFunctionality();
Now, this works fine for the initial load, however I'd also like this to work for every button that is to be created in the future. To do this, I added:
$("#RestoreDefaultsButton").on("restoreFunctionality", function(event) {
$("#RestoreDefaultsButton").removeProp("disabled").attr("onClick", "OnRestoreDeviceClientClick()");
});
This, however, does not work for me but also does not provide any sort of console error message telling me why it won't work. I can't seem to find an example of what I want. I see examples in the jquery doc where it can be called by clicking somewhere or something like that, however what I want is for it to just simply "work". Just look for new instances of that button ID and make the changes.
Is on() not the function I want to use in jquery 1.11.1? Am I somehow using this incorrectly? Any guidance to point me in the right direction would help.
Edit for clarification:
I am not trying to edit the same button multiple times in multiple locations. I am trying and willing to create code individually for each button that comes up, given I know the ID of each one.
Here is an example of something I have that is currently working:
The line of code for the button reads:
<input type="button" name="RestoreDefaultsButton" value="Submit"
id="RestoreDefaultsButton" disabled="disabled" class="aspNetDisabled InlineButtonStyle">
The code that I am using and that actually works just fine is now:
$("body").on("click", "#RestoreDefaultsButton", restoreDefaultFunctionality());
and restoreDefaultFunctionality() is simply:
$("#RestoreDefaultsButton").removeProp("disabled").attr("onClick", "OnRestoreDeviceClientClick()");
Again, the above code works just fine. What I seem to have trouble with is that not all of my buttons are present on load, I may click a link that loads a model on the same page/url with a form that has additional buttons. That button might read:
<input type="button" name="OpenToolkitButton" value="Submit" id="OpenToolkitButton" disabled="disabled" class="aspNetDisabled InlineButtonStyle">
Which is almost exactly the same as the original example, it's just been loaded after the script ran for the first time.
What I am looking for is a solution to make all individually specified buttons that I need, when they occur, to have that disabled removed and a specific onclick function added.
It appears that you have several things wrong and you are using .on() incorrectly.
First, ids in your document must be unique. You cannot have multiple DOM elements with the same id. That is both illegal HTML and will not correctly work with selectors. So, if you're trying to detect future "#RestoreDefaultsButton" objects in addition to the one you already have, you will have to change that because you can't have more than one and still have selector code work correctly. Usually, you want to use a class name instead of an id when you want to find multiple objects of the same type.
Second, your use of .on() is simply not correct. .on() allows you to register a callback function that will be called when a certain DOM event is triggered. So, when you do this:
$("#RestoreDefaultsButton").on("restoreFunctionality", fn);
You are asking for jQuery to call your function when the single "#RestoreDefaultsButton" object triggers the "restoreFunctionality" DOM event. Since "restoreFunctionality" is not a built-in DOM event, the only way that could ever trigger is if you triggered the event yourself.
The usual solution to modifying newly created objects that are inserted into the DOM is to go find the code that creates those objects and insert a function call (to call your own function that can find and "patch up" the newly created DOM objects right AFTER the newly created DOM objects have been created.
The newest browser versions allow you to register a callback to be notified when certain types of objects are added to the DOM so you could get notified automatically. These notifications are call MutationObservers (doc here). Unfortunately, those events are only implemented in the latest browsers (IE11) so you generally can't solely rely on them for a general web page.
Your click handler assignment could probably be solved with delegated event handling. In delegated event handling for dynamically created objects, you find a persistent object (that is not dynamically created) that will be in the parent chain of your dynamically created element and you bind the click event handler to that parent. Since click events "bubble" up the parent chain, the click event will be seen by the parent. Using the delegated form of .on() that works like this:
$("static parent selector").on("click", "dynamic element selector", fn);
You can then handle the event without worrying about the timing of when the dynamic element is created/destroyed, etc...
You can read more about delegated event handling in these references:
Does jQuery.on() work for elements that are added after the event handler is created?
jQuery .live() vs .on() method for adding a click event after loading dynamic html
jQuery .on does not work but .live does
Are you triggering the "restoreFunctionality" event after your ajax forms are built?
$("#RestoreDefaultsButton").trigger("restoreFunctionality");
Forces it to be synchronous if you have more to do after the call and before you finish the function
$("#RestoreDefaultsButton").triggerHandler("restoreFunctionality");

Javascript Event Priority

With the different ways of adding events in javascript, do any of them take priority like css classes do? For example will an inline onclick even always fire before one added with addEventListener?
If not, is there any way to give an event priority?
Yes
An inline onclick handler is going to bind as the DOM is loading
Whereas anything you add with .on or .addEventListener will have to wait for the DOM element to load first.
See here: http://jsfiddle.net/DmxNU/
Your html
click
Your js (jQuery in this case)
$(function() {
$("a").click(console.log.bind(console, "world"));
});
Output
hello
world
Explanation
I'm not sure where this would be documented, but think about it this way. If you're going to use the DOM API to query an element and then add an event listener, that element has to be loaded first. If that element already contains an onclick attribute, that will already be attached first.
JavaScript events don't take any priorities. When and event is fired it is added to an event queue and executed as soon as possible.
You can claim that it is a general rule that onclick attribut events will always trigger first, and that will always be true, but it's not because they are prioritized.
Quoted from #Kevin B's comment

Does this code need to be in a document.ready?

The document.ready is used to execute code after the DOM is fully loaded. This can be used to attach event handlers to elements on the page e.g
$(function(){
$('#somediv').click(function(){
});
})
<div id="somediv"> </div>
Internally, jQuery hooks up to DOMContentLoaded and window.onload as a fallback. In IE's case an attempt is made to scroll the viewport over and over until successful.
I have a few questions, my first one being, when binding event handlers to the document itself, is it necessary to put that code in a document.ready ? I have always been writing the code below without wrapping it in a document.ready
$(document).keydown(function(e){
if (e.which == 39) {
alert( "right arrow pressed" );
return false;
}
});
And as you can see, it works. My understanding is, since this code doesn't hook up to any elements within the document, but the document itself, there's no need to wrap it in a document.ready handler. Another reason i don't wrap it is because i used to do the same in vanilla javascript the equivalent would be the code below, which also works.
document.onkeydown = function(){
var keyCode = event.keyCode || event.which;
if (keyCode == 39) {
alert( "right arrow pressed" );
return false;
}
}
I've seen numerous posts where people wrap it in a document.ready, is there any downside of not wrapping this code in document.ready ?
Also i think this question stems from my lack of clarity of what happens during this time when the DOM is being constructed, so if someone can explain what happens during the period right before the DOM is ready. To me the document is ready when the html has been parsed and converted into a DOM tree, or is there more to it ?
In summary, here are my questions
When binding event handlers to the document itself, is it
necessary to put that code in a document.ready.
Are there any downsides to not wrapping the code in the document.ready ?
What sequence of events take place when the document is being constructed, right before the document.ready is fired ?
If you are binding to the document itself, you don't need to wait until it is ready. There shouldn't be any downsides to not wrapping it in document.ready in this case.
document.ready gets fired when the DOMReady event is triggered by the browser, or when a specific test is successful for versions of browsers that don't support the DOMReady event.
Additional information. (5/22/12)
Most modern browsers implement the DOMContentLoaded event which fires when all elements defined on the document are ready to be manipulated by javascript. Other browsers either rely on a setTimeout loop that continuously checks the readystate of the document or binds directly to the onreadystatechanged method of the document (taken from jquery core). The document itself is ready to be manipulated before javascript is ever executed, therefore you never need to wait when binding directly to the document.
The only gotcha here is that if the code interacts with elements other than the document, there is a chance that the event could be triggered on the document before those elements exist. It is very unlikely for that to happen, but it can happen. If that is something that can happen with your code, then it makes sense to place it inside of $(document).ready() to prevent that scenario. Your sample doesn't warrant being placed inside of $(document).ready().
The point of $(document).ready is to execute code after the entire document has been parsed.
You only need to use it if you want to use elements that don't exist yet.
(eg, if your script is in the <head>)
If the elements you're using already exist (either because they're global or because your <script> is below them), you don't need it.
The only drawback of not binding an event to the document in a document.ready block would be that it will be possible to fire the event before all the page content has been loaded, which may not be what you want.
This event gets triggered when the DOM hierarchy has been fully
constructed i.e. all assets such as images have been completely
received.
You asked:
When binding event handlers to the document itself, is it necessary to put that code in a document.ready?
Answer: Nope. When using code that rely on the value of CSS style properties, it's important to reference external stylesheets or embed style elements before referencing the script in which your code resides or just before document.ready() block.
Are there any downsides to not wrapping the code in the document.ready ?
Answer: No. But when you've to create elements inside your documents by using JavaScript, then should wait for sake until your DOM gets ready. For this, you should put your code inside document.ready() block.
What sequence of events take place when the document is being constructed, right before the document.ready is fired ?
Answer: Before document.ready gets fired, DOMContentLoaded is already triggered by browser.
When using actions to elements or calling them (that will be generated in DOM or don't exist yet) you need to use $(document).ready
In addition to the answers: you can mere use jquery live function (instead of keydown, etc.) to be free of the situation 'DOM elements must be finished'.
So the next must work properly:
$( "#somediv" ).live( 'keydown', function(){ ... } );
In this case jQuery binds the event when it is possible. You don't have a pain to place all bindings in one (ready) function, your bindings can be placed in independent parts of your HTML page or Javascript files.
So, the result answer is: no, you don't need to place your code in document.ready when you use the mentioned function.
Update
In the last versions of jQuery (>= 1.7) use on() function instead of live() because the last one is depricated. So, it's not necessary to place event bindings into ready().
1. When binding event handlers to the document itself, is it necessary to put that code in a document.ready?
No. In fact, the 'on' methods for binding in JQ can delegate at the document so you could use those at any time on any element safely as long as there wasn't a lot of bubbling being stopped at container elements with stopPropagation.
2. Are there any downsides to not wrapping the code in the document.ready?
Only that scripts in the head might try to hit HTML that isn't there yet. The converse is that HTML might be ready and getting events from the user before the doc is. See 'on' methods or google 'event delegation' for having your cake and eating it too where events are concerned (the caveat is libraries that use stopPropagation stupidly). document.ready is mostly just a way to be certain your code is firing when the HTML is ready to be hit. It's not necessary for code that falls at the bottom of the body tag unless (maybe) you're hitting body itself with something.
3. What sequence of events take place when the document is being constructed, right before the document.ready is fired ?
At the point that document ready is fired, all tags have been parsed and the layout dimensions have been established. Images do not need to have fully loaded, and I'm only guessing, but I suspect non-layout impacting CSS may not be in effect yet in some browsers. An element is considered 'ready' when its closing tag has been read and executed on by the HTML parser. JS in script tags must be handled by an interpreter before HTML parsing can continue, which is why we tend to put our code at the bottom of the doc nowadays anyway, for faster perceived loading time.

Categories

Resources