I have often used, and seen recommended, dom-access structures like this for adding content to pages dynamically:
loader = document.createElement('script');
loader.src = "myurl.js";
document.getElementsByTagName('head')[0].appendChild(loader);
Now, by chance, I find that this works in Google chrome:
document.head.appendChild(loader);
A little more investigation, and I find that this works, apparently cross-browser:
document.body.appendChild(loader);
So my primary question is: are there any reasons why I shouldn't attach elements to the BODY like this?
Also, do you think document.head will become more widely supported?
I can’t see any reason why it would matter in practice whether you insert your <script> elements into the <head> or the <body> element. In theory, I guess it’s nice to have the runtime DOM resemble the would-be static one.
As for document.head, it’s part of HTML5 and apparently already implemented in the latest builds of all major browsers (see http://www.whatwg.org/specs/web-apps/current-work/multipage/dom.html#dom-document-head).
document.body is part of the DOM specification, I don't see any point why not to use it. But be aware of this:
In documents with contents, returns the element, and in frameset documents, this returns the outermost element.
(from https://developer.mozilla.org/en/DOM/document.body)
document.head currently isn't defined in any DOM specification (apparently I was wrong on that, see Daniel's answer), so you should generally avoid using it.
I tried implementing this code and ran into a bit of trouble, so wanted to share my experience.
First I tried this:
<script>
loader = document.createElement('script');
loader.src = "script.js";
document.getElementsByTagName('head')[0].appendChild(loader);
</script>
And in the script.js file I had code such as the following:
// This javascript tries to include a special css doc in the html header if windowsize is smaller than my normal conditions.
winWidth = document.etElementById() ? document.body.clientWidth : window.innerWidth;
if(winWidth <= 1280) { document.write('<link rel="stylesheet" type="text/css" href="style/screen_less_1280x.css">'); }
The problem is, when I did all of this, the code wouldn't work. Whereas it did work once I replaced the script loader with simply this:
<script src="script.js"></script>
That works for me, so problem solved for now, but I would like to understand the difference between those two approaches. Why did one work and not the other?
What's more is that in script.js I also have code such as:
function OpenVideo(VideoSrcURL) {
window.location.href="#OpenModal";
document.getElementsByTagName('video')[0].src=VideoSrcURL;
document.getElementsByTagName('video')[0].play();}
And that code does work fine regardless of which way I source the script in my html file.
So my window resizing script doesn't work, but the video stuff does. Therefore I'm wondering if the difference in behavior has to do with "document" object...? Maybe "document" is referencing the script.js file instead of the html file.
I don't know. Thought I should share this issue in case it applies to anyone else.
Cheers.
The answers given as far as now focus two different aspects and are both very useful.
If portability is a requirement for you, in documents not under your ownership where you can't control the DOM coherence, it may be useful to check for the existence of the element you have to append the script to; this way, it will works also when the HEAD section has not been explicitly created:
var script = document.createElement('script');
var parent = document.getElementsByTagName('head').item(0) || document.documentElement;
parent.appendChild(script);
Apart of CORS policy and other easily detectable logic errors, I don't see cases in which this DOM manipulation task should fail, so checking for document.body as a fallback is not necessary by my point of view.
As other users outlined too, document.head was not widely supported yet, at the time the question was posted, as it is a HTML5 spec, while document.body is, that's why you saw the latter working on all the browsers.
So you could get the HEAD with:
document.head || document.getElementsByTagName('head').item(0)
but I don't see it very useful, as the latter won't ever be wrong and is a readable and standard DOM query, unless you find out that document.head is faster from a performance perspective.
Another possible advantage I can see about this form is a code transition from older JavaScript to a more modern HTML5 compliant code, brief and more readable.
If compatibility is a must, you could use tricks adopted by libraries for which portability is mandatory (e.g. solutions from Google, Yandex and others):
var parent = document.getElementsByTagName('head')[0] || document.documentElement.firstChild;
parent.appendChild(script);
This way, in the improbable case the HEAD element does not exist while the BODY (or another element) does, you are sure to append the script to it.
I also add a useful bit: the HTTP request of the script source is sent from the browser when the src attribute is set and it is appended to the DOM. No matter in which order these two conditions are met, the last of these two events causes the HTTP request to be dispatched.
Related
I have a situation where I am loading external HTML into a WYSIWYG editor on a web page. The external HTML is from a trusted source, and includes what ever is between two specific <div> tags from a number of different pages.
My problem is that some of the pages contain inline javascript event handlers. So when I am working in the WYSIWYG editor, certain events cause the execution of this js.
Mostly it doesn't do anything other than fill up the console with errors saying ... is not defined, and this may be all it is, and that wouldn't be a problem. But still, it's messy, and I don't know if there isn't a page somewhere that might execute something like alert(...) which could get extremely annoying. The code is all trusted, but detached from its intended context, it could produced undesirable results.
I want to find a way to globally prevent this execution, preferably without modifying the inline script. I could do something like attach =false; to each handler, but then I have to check all the incoming elements, and even with regular expressions, this will degrade performance. Also, I then would have to remove it before submitting the edited HTML back to the server, which seems like a major pain, and difficult to do flawlessly.
Is there a way to prevent this online code from being executed within this particular context?
Depending on what browser you are trying to support, you can checkout 'Content Security Policy' headers. Checkout http://caniuse.com/contentsecuritypolicy for details on browser support.
If you target browser is in the list, CSP can do exactly what you are looking for. It will disable event handlers by default. It will block execution of any code embedded within on the page in addition to blocking event handlers. So you will need to move all your js code, if present on the html page to a separate js, specify that filename in the safe-list and load your js from there.
CSP are set as Http headers but with the new specification it can be set using meta tags as well. Checkout https://dvcs.w3.org/hg/content-security-policy/raw-file/tip/csp-specification.dev.html#html-meta-element--experimental.
For WebKit supported browsers(Chrome/Safari) something like
would restrict load from any external source. You can add a list of accepted sources to the list and explore what works for you.
For firefox, though its there in the specification, I do not think the meta tag is supported yet.
https://developer.mozilla.org/en-US/docs/Security/CSP/Introducing_Content_Security_Policy.
So to summarize, as long as you can set headers for the Web-Server that is hosting the web page (unless you only want Chrome/Safari support), and your target browser supports CSP, you might give it a shot.
I copied this from sitepoint forum, a question similar to yours. This takes an approach of pulling div out of the parent and putting it back on. If this is what you want here is the example code:
var div = document.getElementById('div');
var nextSibling = div.nextSibling;
var parent = div.parentNode;
parent.removeChild(div);
function reCreateElement() {
if (nextSibling) {
parent.insertBefore(div, nextSibling);
} else {
parent.appendChild(div);
}
}
setTimeout(reCreateElement, 100);
// Below is the stuff we don't want to run!
document.getElementById('div').onclick = function() {
alert('CLICKED!');
}
I'm working on a Google Chrome extension that manipulates a webpage, but after it is either partially loaded (the DOM) or fully loaded (with images).
It seems that many sites nowadays use the
<!DOCTYPE html>
declaration, or some variation of it, but many others do not. The question is mainly about HTML doctypes...I'm not sure about the others.
Is it safe to assume that if a webpage does not have the DOCTYPE declaration, then $(window).load(); will not be fired?
In the beginning I was using $(document).ready(); (for when the DOM is loaded), but later switched to $(window).load(); (to let the images load too).
The thing is, now $(window).load(); does not seem to work if there is no DOCTYPE. $(document).ready(); seems to work on all pages, regardless of whether a DOCTYPE is declared or not.
Maybe this can be useful for others with this same issue. I searched a bit and didn't find a decisive answer. It seems that I will end up using something like this:
if (window.document.doctype != null) {$(window).load(checkEntries);}
if (window.document.doctype == null) {$(document).ready(checkEntries);}
I guess my question is... Is this normal to have to check for the DOCTYPE to know which event to use? Or am I missing something here?
Basically, why does $(window).load(); seem not to fire if there's no DOCTYPE declaration?
Basically, you shouldn't be using $(window).load(), since it's not fully supported. If you really need it, then your solution above is the best you can do. The jQuery page sums up the caveats nicely:
Caveats of the load event when used with images
A common challenge developers attempt to solve using the .load()
shortcut is to execute a function when an image (or collection of
images) have completely loaded. There are several known caveats with
this that should be noted. These are:
It doesn't work consistently nor reliably cross-browser
It doesn't fire correctly in WebKit if the image src is set to the same src as before
It doesn't correctly bubble up the DOM tree
Can cease to fire for images that already live in the browser's cache
URL: http://api.jquery.com/load-event/
The .ready() method is generally incompatible with the <body onload=""> attribute. If load must be used, either do not use .ready() or use jQuery's .load() method to attach load event handlers to the window or to more specific items, like images.
I've noticed that jQuery can create, and access non-existent/non-standard HTML tags. For example,
$('body').append('<fake></fake>').html('blah');
var foo = $('fake').html(); // foo === 'blah'
Will this break in some kind of validation? Is it a bad idea, or are there times this is useful? The main question is, although it can be done, should it be done?
Thanks in advance!
You can use non-standard HTML tags and most of the browsers should work fine, that's why you can use HTML5 tags in browsers that don't recognize them and all you need to do is tell them how to style them (particularly which tags are display: block). But I wouldn't recommend doing it for two reasons: first it breaks validation, and second you may use some tag that will later get added to HTML and suddenly your page stops working in newer browsers.
The biggest issue I see with this is that if you create a tag that's useful to you, who's to say it won't someday become standard? If that happens it may end up playing a role or get styles that you don't anticipate, breaking your code.
The rules of HTML do say that if manipulated through script the result should be valid both before and after the manipulation.
Validation is a means to an end, so if it works for you in some way, then I wouldn't worry too much about it. That said, I wouldn't do it to "sneak" past validation while using something like facebook's <fb:fan /> element - I'd just suck it up and admit the code wasn't valid.
HTML as such allows you to use any markup you like. Browsers may react differently to unknown tags (and don't they to known ones, too?), but the general bottom line is that they ignore unknown tags and try to render their contents instead.
So technically, nothing is stopping you from using <fake> elements (compare what IE7 would do with an HTML5 page and the new tags defined there). HTML standardization has always been an after-the-fact process. Browser vendors invented tags and at some point the line was drawn and it was called HTMLx.
The real question is, if you positively must do it. And if you care whether the W3C validator likes your document or not. Or if you care whether your fellow programmers like your document or not.
If you can do the same and stay within the standard, it's not worth the hassle.
There's really no reason to do something like this. The better way is to use classes like
<p class = "my_class">
And then do something like
$('p.my_class').html('bah');
Edit:
The main reason that it's bad to use fake tags is because it makes your HTML invalid and could screw up the rendering of your page on certain browsers since they don't know how to treat the tag you've created (though most would treat it as some kind of DIV).
That's the main reason this isn't good, it just breaks standards and leads to confusing code that is difficult to maintain because you have to explain what your custom tags are for.
If you were really determined to use custom tags, you could make your web page a valid XML file and then use XSLT to transform the XML into valid HTML. But in this case, I'd just stick with classes.
I'm retrieving an entire HTML document via AJAX - and that works fine. But I need to extract certain parts of that document and do things with them.
Using a framework (jquery, mootools, etc) is not an option.
The only solution I can think of is to grab the body of the HTML document with a regex (yes, I know, terrible) ie. <body>(.*)</body> put that into the current page's DOM in a hidden element, and work with it from there.
Is there an easier/better way?
Update
I've done some testing, and inserting an entire HTML document into a created element behaves a bit differently across browsers I've tested. For example:
FF3.5: keeps the contents of the HEAD and BODY tags
IE7 / Safari4: Only includes what's between ...
Opera 10.10: Keeps HEAD and everything inside it, Keeps contents of BODY
The behavior of IE7 and Safari are ideal, but different browsers are doing this differently. Since I'm loading a predetermined HTML document I think I'm going to use the regEx to grab what I want and insert it into a DOM element - unless someone has other suggestions.
Elements can exist without being in the page itself. Just dump the HTML into a dummy div.
var wrapper = document.createElement('div');
wrapper.innerHTML = "<ul><li>foo</li><li>bar</li></ul>";
wrapper.getElementsByTagName('li').length; // 2
Given your edits, we run into a sticky situation, since you want getElementById. The matter would probably be easy if you could just create a new virtual document via document.implementation.createDocument, but IE doesn't support that at all.
Using a regex is a messy business, since what if we see something like <body><input value="</body>" /></body>? You could probably just make your regex greedy so that it moves on to the last instance of </body>, but if you do end up running into troubles, a more thorough parsing may be necessary. Even if a full framework isn't an option, you might end up wanting to use something like Sizzle, the core of libraries like jQuery, to look for the element you want. Or, if you're really feeling in a purist sort of mood, you could write the recursive search function yourself - but why take that hit if someone else has already taken it?
var response_el = document.createElement('html'), foo;
response_el.innerHTML = the_html_elements_content;
foo = Sizzle('#foo', response_el);
This is generally how I manage progressive enhancement whilst keep the experience clean, but how safe is it? is there potential for a race condition and this not working?
Imagine the simple abstract scenario, you want to display something differently if you have javascript support.. this is generally what I will end up doing:
<div id="test">original</div>
<script type="text/javascript">
var t = document.getElementById('test');
t.innerHTML = 'changed';
</script>
Many may claim you should use a framework and wait for a domready event, and do changes there.. however there is a significant delay where the 'test' element will have already been rendered before the end of the document and the css are ready and a domready triggers.. thus causing a noticable flicker of 'original'.
Is this code liable to race condition failures? or can I guarentee that an element is discoverable and modifiable if it exists before the script?
Thanks in advance.
You can, but there are issues surrounding doing it.
First off, in IE if you attempt to manipulate a node that has not been closed (e.g. BODY before its close tag which should be below your JS) then you can encounter IE's "OPERATION ABORTED" error which will result in a blank page. Manipulation of a node includes appending nodes, moving nodes, etc.
In other browsers the behavior is undefined, however they do usually behave as you would expect. The main issue is that as your page evolves, the page may load/parse/run differently. This may cause some script to run before a browser defines referenced elements have actually been created and made available for DOM manipulation.
If you are attempting to enhance your user perceived performance (i.e. snappiness). I highly suggest that you avoid this path and look into lightening your pages. You can use Yahoo's YSlow/Google's Page Performance Firebug to help you get started.
Google's Page Speed
Yahoo's YSlow
You can manipulate the DOM before it has fully loaded, but it can be risky. You obviously can't guarantee that the bit of the DOM you are trying to manipulate actually exists yet, so your code may fail intermittently.
As long as you only modify nodes which preceed the script block (ie the node's closing tag preceeds the script's opening tag), you shouldn't encounter any problems.
If you want to make sure the operation succeeds, wrap the code in a try...catch block and call it again via setTimeout() on failure.
In Viajeros.com I have a loading indicator working since 8-9 months and I have no problems so far. It looks like this:
<body>
<script type="text/javascript">
try {
document.write('<div id="cargando"><p>Cargando...<\/p><\/div>');
document.getElementById("cargando").style.display = "block";
} catch(E) {};
</script>
Accessing the DOM prematurely throws exceptions in IE 5 and Navigator 4.