Why does DOM appendChild() fire on('load', ...) but jQuery append() does not? - javascript

I have the following to snippets of code:
$(document).ready(function() {
document.head.appendChild(
$('<script />').attr('src', 'source.js').on('load', function() {
...
})[0]
);
});
This will fire the load handler.
Whereas using the normal jQuery append():
$(document).ready(function() {
$('head').append(
$('<script />').attr('src', 'source.js').on('load', function() {
...
})
);
});
This will not fire the load hander.
What am I missing: why does jQuery append() not work?
Is using document.head.appendChild() a bad idea?
NOTE: I can't use $.getScript(). The code will run on a local file system and chrome throws cross site script errors.
Update
Some people had trouble reading the compact style, so I used extra line feeds to clarify which objects where calling which methods. I also made it explicit that my code is inside a $(document).ready block.
Solution
In the end I went with:
$(document).ready(function() {
$('head')[0].appendChild(
$('<script />').attr('src', 'source.js').on('load', function() {
…
})[0]
);
});
I think #istos was right in that something in domManip is breaking load.

jQuery is doing some funny business in its DOM manipulation code. If you look at jQuery's source, you'll see that it uses a method called domManip() inside the append() method.
This domManip() method creates a document fragment (it looks like the node is first appended to a "safe" fragment) and has a lot of checks and conditions regarding scripts. I'm not sure why it uses a document fragment or why all the checks about scripts exist but using the native appendChild() instead of jQuery's append() method fires the event successfully. Here is the code:
Live JSBin: http://jsbin.com/qubuyariba/1/edit
var url = 'http://d3js.org/d3.v3.min.js';
var s = document.createElement('script');
s.src = url;
s.async = true;
$(s).on('load', function(e) {
console.log(!!window.d3); // d3 exists
$(document.body).append('<h1>Load fired!</h1>');
});
$('head').get(0).appendChild(s);
Update:
appendChild() is a well supported method and there is absolutely no reason not to use it in this case.

Maybe the problem is when you choose DOM appendChild, actually you called the function is document.on('load',function(){});, however when you choose jQuery append(), your code is $('head').on('load', function(){}).
The document and head are different.
You can type the code below:
$(document).find('head').append($('<script />').attr('src', 'source.js').end().on('load', function() {
...
}));

You should probably make sure that the jquery append is fired when the document is ready. It could be that head is not actually in the dom when the append fires.

you don't have to ditch jquery completely, you could use zeptojs. Secondly, I couldn't find out how and why exactly this behavior is happening. Even though i felt answer was to be found in links below. So far i can tell that if you insert element before definig src element then load won't fire.
But for manual insertion it doesn't matter. (????)
However, what i was able to discover is that if you use appendTo it works.
Code :http://jsfiddle.net/techsin/tngxnkk7/
var $ele = $('<script />').attr('src', link).load(function(){ abc(); }) ).appendTo('head');
New Info: As is understood adding script tag to dom with src attribute on it, initiates the download process of script mentioned in src. Manual insertion causes page to load external script, using append or appendTo causes jquery to initiate downloading of external js file. But event is attached using jquery and jquery initiates download then event won't fire. But if it's the page itself initiates the download then it does. Even if event is added manually, without jquery, adding via jquery to dom won't make it fire.
Links in which i think should be the answer...
Append Vs AppendChild JQuery
http://www.blog.highub.com/javascript/decoding-jquery-dommanip-dom-manipulation/
http://www.blog.highub.com/javascript/decoding-jquery-dommanip-dom-manipulation/
https://github.com/jquery/jquery/blob/master/src/manipulation.js#L477-523
http://ejohn.org/blog/dom-documentfragments/

Related

Can someone explain to me how this is possible? (Picture in comments)

So I'm trying to link up my html and javascript files in notepad++, but it isn't working properly.
I wanted to know how it is possible that it writes test, but doesn't remove the div. Can anyone explain this? Thanks in advance!
1, jQuery isn't linked. Meaning, you don't have <script type='text/javascript' src='myjQueryfile.js'></script> in your HTML, you'll want to put it before your script.
2:
Because the element with the ID of blue, doesn't exist yet. The DOM - basically the object of your HTML - has yet to be constructed when your script is run, which in this case is the top of the page, before blue comes into existence. You'll want to use an event to fix this, typically $(function(){ ... }); which will execute your code when the DOM is ready.
Also, document.write just writes code then and there, meaning exactly where the document.write calls is made, the HTML will be outputted.
You should have linked jquery. You're trying to use it without having it linked.
The script is loaded in the head. At the time the script executes the body of the document is not built, so nothing is removed. If you were to use the document.ready callback (and had properly included jQuery) it would work
$(function(){ $("#blue").remove(); });
A plain js version of this is
window.onload = function(){
var b = document.getElementById("blue");
b.parentNode.remove(b);
};
At the time the script runs, only the portion of the document up to the <script> tag has been loaded. You need to delay until the DOM has fully loaded before the script can target the DOM:
document.addEventListener("DOMContentLoaded", function(event) {
$("#blue").remove();
});

Scripts not running when using get function in jquery

I'm using the $.get() function to extract some data from my site. Everything works great however on one of the pages the information I need to extract is dynamically created and then inserted into a <div> tag.
So in the <script> tag, a function is run and then the data is inserted into <div id="infoContainer"></div>. I need to get the information from #infoContainer, however when I try to do so in the $.get() function, it just says it's empty. I have figured out that it is because the <script> tag is not being run. Is there another way to do this?
Edit:I am making a PhoneGap application for my site using jQuery to move content around so it's more streamlined for mobiles.
This is the code on my page:
$(document).ready(function () {
var embedTag = document.createElement("embed");
var infoContainer = document.getElementById("infoContainer");
if (infoContainer != null) {
embedTag.setAttribute("height", "139");
embedTag.setAttribute("width", "356");...other attributes
infoContainer.appendChild(embedTag);
});
});
As you can see, it puts content into the #infoContainer tag. However, when I try to extract info from that tag through the get function it shows it as empty.I have done the same to extract headings and it works great. All I can gather is the script tag is not firing.
This should provide you the contents of the element:
$('#infoContainer').html();
Maybe your script is executing before the DOM is loaded.
So if you are manipulating DOM elements you should wait till DOM is loaded to manipulate it. Alternately you can place your script tag at the end of your HTML document.
// These three are equivalent, choose one:
document.addEventListener('DOMContentLoaded', initializeOrWhatever);
$( initializeOrWhatever );
$.ready( initializeOrWhatever );
function initializeOrWhatever(){
// By the time this is called, the DOM is loaded and you can read/write to it
$.getJSON('/foo/', { myData: $('#myInput').val() }, onResponse);
function onResponse(res){
$(document).html('<h1>Hello '+res+'</h1>');
};
};
Otherwise... post more specifics and code
You have no ID to reference. Try setting one before you append
embedTag.setAttribute("id", "uniqueID");
It looks like you are wanting to use jQuery, but your example code has vanilla JavaScript. Your entire function can be simplified using the following jQuery (jsFiddle):
(function () {
var embedTag = $(document.createElement("embed"));
var infoContainer = $("#infoContainer");
if (infoContainer.length) {
embedTag.attr({"height": 139, "width": 356});
infoContainer.append(embedTag);
}
console.log(infoContainer.html()); // This gets you the contents of #infoContainer
})();
jQuery's .get() method is for sending GET requests to a server-side script. I don't think it does what you are wanting to do.

Div is not created before javascript run

I have a question about javascript/html.
First, I have this:
var post = document.body.getElementsByClassName("post");
var x=post[i].getElementsByClassName("MyDiv")[0].innerHTML;
I get from the debugger that x is not defined, it doesn't exists.
This javascript function runs onload of the body. I am sure that I gave the right classnames in my javascript, so it should find my div.
So, I read somewhere that sometimes javascript does not find an element because it is not yet there, it is not yet created in the browser ( whatever that means).
Is it possible that my function can't find the div with that classname because of this reason?
Is there a solution?
So, I read somewhere that sometimes javascript does not find an element because it is not yet there, it is not yet created in the browser ( whatever that means).
Browsers create the DOM progressively as they get the markup. When a script element is encountered, all processing of the markup stops (except where defer and async have an effect) while the script is run. If the script attempts to access an element that hasn't been created yet (probably because its markup hasn't been processed yet) then it won't be found.
This javascript function runs onload of the body.
If that means you are using something like:
<body onload="someFn()"...>
or perhaps
<script>
window.onload = function() {
someFn();
...
}
</script>
then when the function is called, all DOM nodes are available. Some, like images, may not be fully loaded, but their elements have been created.
If it means you have the script in the body and aren't using the load event, you should move the script to the bottom of the page (e.g. just before the closing body tag) and see if that fixes the issue.
Okay, instead of calling functions with
body onload, use jQuery's ready() function, or, if you don't want to use jQuery, you can use pure javascript, but this is up to you:
// jQuery
$(document).ready(function() {
var post = document.getElementsByClassName("post"),
x = post[i].getElementsByClassName("MyDiv")[0].innerHTML;
});
// JavaScript
window.onload = function initialization() {
var post = document.getElementsByClassName("post"),
x = post[i].getElementsByClassName("MyDiv")[0].innerHTML;
}
A few side notes, I don't know what the use of innerHTML
is, and also if you're doing a for loop with i then definitely
post that code, that's kind of important.
After some discussion, my answer seems to have worked for you, but you can also place your script at the end of your body tag as #RobG has suggested.

How can I stop a JavaScript from executing if an element exists?

I have a Firefox addon that injects/executes a jQuery script on every page at sub.example.com. The script doesn't work on one page of the site, because of bad design. Is there any way to stop the script from being executed if a certain element is located on the page?
EDIT: The script I am using has to be executed before the DOM loads. Is there any way to access the HTML file itself and find out if the element exists?
Since jQuery collections are just beefed up arrays, they each have a length property, which tells you how many elements it has matched:
jQuery(document).ready(function($)
{
if ( $('#someElement').length ) return;
// All of your code should go here...
});
Since you're using jQuery, I assume your script is wrapped in a $(document).ready callback, if so:
$(document).ready(function() {
if ($('#breakOnThisElem').length) {
return;
}
});
If your code isn't wrapped in a function like this: change it! :)

Best way of unobtrusive onload in plain JavaScript

What is the best unobtrusive way of invoking something after the page is being loaded in plain JavaScript? Of course in jQuery I would use:
$(document).ready(function(){...});
but I am not sure about the most reliable approach in plain js.
Clearly
window.onload = ...
is not proper solution, because it would overwrite previous declaration.
What I am trying to do is to insert an iframe into a div after the page is loaded, but maybe there are actually better ways of doing it. My plan is to do something like:
window.onload = function(divId){
var div = document.getElementById(divId);
div.innerHTML = "<iframe src='someUrl' .. >";
}
EDIT:
Apologies for not including all necessary details.
The script is not for my website - the idea is to show a part of my site (a form) on external web sites. The priority is to minimize the effort someone has to put to use my code. That is why I would like to keep everything in js file and absolutely nothing in <script> - except of <script src="http://my.website/code.js" />. If I change URL of an iframe or I would like to add some features, I would like to update the code on all other web sites without asking them to make any changes.
My approach might be wrong - any suggestions are very welcome.
//For modern browsers:
document.addEventListener( "DOMContentLoaded", someFunction, false );
//For IE:
document.attachEvent( "onreadystatechange", someFunction);
`attachEvent` and `addEventListener` allow you to register more than one event listener for a particular target.
See:
https://developer.mozilla.org/en/DOM/element.addEventListener
Also definitly worth looking at how jQuery does it:
http://code.jquery.com/jquery-1.7.js Search for bindReady.
Use window.addEventListener and the events load or DOMContentLoaded:
window.addEventListener('DOMContentLoaded',function(){alert("first handler");});
window.addEventListener('DOMContentLoaded',function(){alert("second handler");});
object.addEventListener('event',callback) will insert an event listener into a queue for that specific object event. See https://developer.mozilla.org/en/DOM/element.addEventListener for further information.
For IE5-8 use window.attachEvent('event',callback), see http://msdn.microsoft.com/en-us/library/ms536343%28VS.85%29.aspx. You can build yourself a little helper function:
function addEventHandler(object,szEvent,cbCallback){
if(typeof(szEvent) !== 'string' || typeof(cbCallback) !== 'function')
return false;
if(!!object.addEventListener){ // for IE9+
return object.addEventListener(szEvent,cbCallback);
}
if(!!object.attachEvent){ // for IE <=8
return object.attachEvent(szEvent,cbCallback);
}
return false;
}
addEventHandler(window,'load',function(){alert("first handler");});
addEventHandler(window,'load',function(){alert("second handler");});
Note that DOMContentLoaded isn't defined in IE lesser 9. If you don't know your recipient's browser use the event load.
Just put your script include at the very end of the document, immediately before or after the ending </body> tag, e.g.:
(content)
(content)
<script src="http://my.website/code.js"></script>
</body>
</html>
All of the markup above the script will be accessible via the usual DOM methods (reference). Obviously, not all ancillary resources (images and such) will be fully loaded yet, but presumably that's why you want to avoid the window load event (it happens so late).
The only real purpose of ready-style events is if you don't control where the script gets included (e.g., libraries) or you need to have something execute prior to the page load and something else after the page load, and you want to avoid having two HTTP requests (e.g., for two different scripts, one before load and one after).

Categories