I am trying to get the targeted element with the pseudo-class :target after document load.
I created the following example to illustrate the problem.
<!DOCTYPE html>
<html>
<head>
<script>
document.addEventListener("DOMContentLoaded",function(){
console.log(document.querySelector(":target"));
});
</script>
</head>
<body>
<div id="test"></div>
</body>
</html>
If I load test.html, then the console outputs :
null
If I load test.html#test on Chrome and Opera, then the console outputs :
null
If I load test.html#test on Firefox and IE11, then the console outputs :
<div id="test"></div>
My questions are :
Which browsers have the correct behaviour ?
Does DOMContentLoaded is the correct event to call querySelector(":target") ?
Is there another way to get targeted element after document load ?
PS : I succeeded to fix the problem on Chrome and Opera thanks to setTimeout but It is not a good solution. Does someone has a better idea ?
EDIT : I found a similar issue with JQuery Selecting :target on document.ready()
This is a known issue with WebKit- and Blink-based browsers that has never been directly addressed. The workaround suggested by web-platform-tests is to request an animation frame, which only happens after page rendering, at which point the :target pseudo seems to match successfully:
async_test(function() {
var frame = document.createElement("iframe");
var self = this;
frame.onload = function() {
// :target doesn't work before a page rendering on some browsers. We run
// tests after an animation frame because it may be later than the first
// page rendering.
requestAnimationFrame(self.step_func_done(init.bind(self, frame)));
};
frame.src = "ParentNode-querySelector-All-content.xht#target";
document.body.appendChild(frame);
})
My testing shows that simply using onload works fine, but the author may be on to something and besides, a single call to requestAnimationFrame() costs practically nothing, so you may as well follow suit.
The following test uses onload (as opposed to DOMContentLoaded, which fires immediately after the DOM tree has been constructed but not necessarily rendered):
data:text/html,<!DOCTYPE html><script>window.onload=function(){console.log(document.querySelector(":target"));};</script><div id="test"></div>#test
The following test uses requestAnimationFrame() in conjunction with onload:
data:text/html,<!DOCTYPE html><script>window.onload=requestAnimationFrame(function(){console.log(document.querySelector(":target"));});</script><div id="test"></div>#test
It looks like Firefox has the ideal behaviour, though maybe not the correct one.
Nevertheless, as an alternative, you can use:
document.addEventListener('DOMContentLoaded', () => document.querySelector(window.location.hash));
and that will work in all browsers.
Related
I'm trying what should have been a simple operation: when a user clicks a link a modal window pops up that's populated with some appropriate data in a string. Here's the HTML for the window:
<!DOCTYPE HTML>
<html>
<head>
<title>Modal Display Window</title>
</head>
<body>
<div id="modal_display_block">REPLACE THIS</div>
</body>
</html>
And this is the Javascript function that calls and populates the window:
function displayCenterBlock(data) {
DispWin = window.open("modal_window.html", "", 'toolbar=no,status=no,width=300,height=300');
DispWin.onload = function() {
DispWin.document.getElementById('modal_display_block').innerHTML = data;
}
}
This works great in every browser I've tried except Internet Explorer. In IE the innerHTML does not get rewritten by the data. Is there some IE-specific trick or tweak I need to apply to get this working in that browser?
Many thanks in advance!
ON EDIT: I've discovered that if I move the element rewrite line out of the onload function it then works fine in IE but not in other browsers. It appears my options are to use some conditional code to rewrite at once for IE and to wait for page load for all other browsers, or to abandon the rewrite element approach and just use a document.write. I get from forum searches people like to discourage document.write but that's looking pretty appealing right now.
Okay, for better or worse this code achieves the goal and appears to work cross browser, even in IE.
DispWin = window.open("", "Display", 'toolbar=no,status=no,width=300,height=300');
DispWin.document.open();
DispWin.document.write(data);
DispWin.document.close();
DispWin.focus();
I get that document.write can re-write the whole page, and that is sometimes bad, but in this case that is exactly what I want: a single small page displaying only what was passed in the data argument. I can style it inline.
Here is the circumstance:
I have 2 pages:
1 x html page
1 x external Javascript
Now in the html page, there will be internal Javascript coding to allow the placement of the window.onload, and other page specific methods/functions.
But, in the external Javascript I want certain things to be done before the window.onload event is triggered. This is to allow customized components to be initialized first.
Is there a way to ensure initialization to occur in the external Javascript before the window.onload event is triggered?
The reason I have asked this, is to attempt to make reusable code (build once - use all over), to which the external script must check that it is in 'order/check' before the Javascript in the main html/jsp/asp/PHP page takes over. And also I am not looking for a solution in jQuery #_#
Here are some of the links on Stack Overflow I have browsed through for a solution:
Javascript - How to detect if document has loaded (IE 7/Firefox 3)
How to check if page has FULLY loaded(scripts and all)?
Execute Javascript When Page Has Fully Loaded
Can someone help or direct me to a solution, your help will be muchness of greatness appreciated.
[updated response - 19 November 2012]
Hi all, thanks for you advice and suggested solutions, they have all been useful in the search and testing for a viable solution.
Though I feel that I am not 100% satisfied with my own results, I know your advice and help has moved me closer to a solution, and may indeed aid others in a similar situation.
Here is what I have come up with:
test_page.html
<html>
<head>
<title></title>
<script type="text/javascript" src="loader.js"></script>
<script type="text/javascript" src="test_script_1.js"></script>
<script type="text/javascript" src="test_script_2.js"></script>
<script type="text/javascript">
window.onload = function() {
document.getElementById("div_1").innerHTML = "window.onload complete!";
}
</script>
<style type="text/css">
div {
border:thin solid #000000;
width:500px;
}
</head>
<body>
<div id="div_1"></div>
<br/><br/>
<div id="div_2"></div>
<br/><br/>
<div id="div_3"></div>
</body>
</html>
loader.js
var Loader = {
methods_arr : [],
init_Loader : new function() {
document.onreadystatechange = function(e) {
if (document.readyState == "complete") {
for (var i = 0; i < Loader.methods_arr.length; i++) {
Loader.method_arr[i]();
}
}
}
},
load : function(method) {
Loader.methods_arr.push(method);
}
}
test_script_1.js
Loader.load(function(){initTestScript1();});
function initTestScript1() {
document.getElementById("div_1").innerHTML = "Test Script 1 Initialized!";
}
test_script_2.js
Loader.load(function(){initTestScript2();});
function initTestScript2() {
document.getElementById("div_2").innerHTML = "Test Script 2 Initialized!";
}
This will ensure that scripts are invoked before invocation of the window.onload event handler, but also ensuring that the document is rendered first.
What do you think of this possible solution?
Thanking you all again for the aid and help :D
Basically, you're looking for this:
document.onreadystatechange = function(e)
{
if (document.readyState === 'complete')
{
//dom is ready, window.onload fires later
}
};
window.onload = function(e)
{
//document.readyState will be complete, it's one of the requirements for the window.onload event to be fired
//do stuff for when everything is loaded
};
see MDN for more details.
Do keep in mind that the DOM might be loaded here, but that doesn't mean that the external js file has been loaded, so you might not have access to all the functions/objects that are defined in that script. If you want to check for that, you'll have to use window.onload, to ensure that all external resources have been loaded, too.
So, basically, in your external script, you'll be needing 2 event handlers: one for the readystatechange, which does what you need to be done on DOMready, and a window.onload, which will, by definition, be fired after the document is ready. (this checks if the page is fully loaded).
Just so you know, in IE<9 window.onload causes a memory leak (because the DOM and the JScript engine are two separate entities, the window object never gets unloaded fully, and the listener isn't GC'ed). There is a way to fix this, which I've posted here, it's quite verbose, though, but just so you know...
If you want something to be done right away without waiting for any event then you can just do it in the JavaScript - you don't have to do anything for your code to run right away, just don't do anything that would make your code wait. So it's actually easier than waiting for events.
For example if you have this HTML:
<div id=one></div>
<script src="your-script.js"></script>
<div id=two></div>
then whatever code is in your-script.js will be run after the div with id=one but before the div with id=two is parsed. Just don't register event callbacks but do what you need right away in your JavaScript.
javascript runs from top to bottom. this means.. if you include your external javascript before your internal javascript it would simply run before the internal javascript runs.
It is also possible to use the DOMContentLoaded event of the Window interface.
addEventListener("DOMContentLoaded", function() {
// Your code goes here
});
The above code is actually adding the event listener to the window object, though it's not qualified as window.addEventListener because the window object is also the global scope of JavaScript code in webpages.
DOMContentLoaded happens before load, when images and other parts of the webpage aren't still fully loaded. However, all the elements added to the DOM within the initial call stack are guaranteed to be already added to their parents prior to this event.
You can find the official documentation here.
As usual, I want to alert users to unsaved changes when leaving a page. I have this test page:
<html>
<head>
<title>Testing</title>
<script language="JavaScript1.1" src="https://127.0.0.1:8443/scripts/base.js"></script>
<script language="JavaScript1.1" src="https://127.0.0.1:8443/scripts/edit.js"></script>
<script language="JavaScript1.1">window.onbeforeupload=moveAway</script>
</head>
<body onLoad="init()">
Google
</body>
</html>
The moveAway function is defined in "edit.js" like this:
function moveAway ()
return "foo";<br>
}
The event doesn't fire, or at least it just leaves the page silently (using IE8, Firefox 15, and Chrome 20). I've tried breakpointing the function in Firebug and it never gets to the breakpoint. I've tried it from the web server (an SSL server, the test version of which runs at 127.0.0.1:8443) and I've tried opening the file directly with the browser (which is why I used absolute URLs for the first two <script> tags). I've tried removing the "src=" attribute from the script tags.
On the other hand, this page has an example which does work (at least in Firefox):
https://web.archive.org/web/20211028110528/http://www.4guysfromrolla.com/demos/OnBeforeUnloadDemo1.htm
There is also a very similar example at MSDN which also works:
http://msdn.microsoft.com/en-us/library/ms536907%28VS.85%29.aspx
I really can't see the difference between what they do and what I'm doing. can anyone tell me why their code works and mine doesn't?
use jQuery bind function.. it works great for me..
see bellow
$(window).bind('beforeunload', function() {
return "Want to leave?";
});
onbeforeupload , really ? it should be onbeforeunload. Is that a spelling mistake, or is that how your actual code is ?
You have a syntax error, the function should be:
function moveAway () {
return "foo";
}
http://jsfiddle.net/DerekL/qDqZF/
$('<img/>').attr('src', "http://derek1906.site50.net/navbar/images/pic3.png").load(function() {
$("body").html("done");
blah blah blah...
})
There I have tested using $("<img/>").load in IE 7, and what I got are these:
When run in counsel, I get this:
"Unable to get value of the property 'attr': object is null or undefined"
When used in my webpage, I get this:
"SCRIPT5007: Unable to get value of the property 'slice': object is null or undefined"
jquery.js, line 2 character 32039
What happened? (Hate IE...)
Ensure that the load function is being executed. I recently dealt with this issue. In IE the load function wasn't firing on cached images. For my purposes I was able to get around this by never allowing the image to cache. ( An ugly solution )
ex:
src="loremIpsum.jpg"
change to:
src="loremIpsum.jpg?nocache=" + new Date().getTime()
Where "nocache" can be changed to anything that makes sense to you.
From the jQuery documentaion:
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"
http://api.jquery.com/load-event/
In IE the load event doesn't always get triggered when the image is cached. Try this:
var img = new Image();
img.src = "http://derek1906.site50.net//navbar/images/pic3.png";
if (img.complete || img.readyState === 4) {
$("body").html("done");
}
else {
$(img).on("load error onreadystatechange",function(e){
if (e.type === "error") {
$("body").html("Image failed to load.");
}
else {
$("body").html("done");
}
});
}
Also, don't forget to wait for the DOMReady event, otherwise $("body") may not exist yet if the image loads fast enough.
jsFiddle
Edit:
I have a plugin that may help simplify image preloading: https://github.com/tentonaxe/jQuery-preloadImages/
So I did some quick testing in jfidde, and pulled out the relevant code and ran it standalone in ie7-8-9. They all ran fine. I can say with confidence that it is not this piece of code that is breaking your page.
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<title> - jsFiddle demo by DerekL</title>
<script type='text/javascript' src='http://code.jquery.com/jquery-1.7.1.js'></script>
<script type='text/javascript'>
$('<img/>').attr('src', "http://derek1906.site50.net//navbar/images/pic3.png").load(function() {
$("body").html("done");
$("<img/>").appendTo("body").attr("src","http://derek1906.site50.net//navbar/images/pic3.png");
});
</script>
</head>
<body>
Loading...
</body>
</html>
Some ideas though:
Wrap any script in the document head that manipulates the DOM in a document.ready call.
$(document).ready(function(){ go(); });
Search your source code for that slice call. If you are trying to manipulate a jQuery collection with .slice() it will break. jQuery collections are Objects, not Arrays. (may or may not be your problem)
Make sure that any code trying to touch that image is called after the .load() method returns. A common mistake is to do something like:
$('<img id="me" />').attr('src', 'my.jpeg')
.load( function(){ $(this).appendTo("body"); } );
alert( $('#me').attr('src') );
If this is the only image on the page, the above script will likely fail because the appendTo() is called asyncronously, and almost certianly after the following lines of code have executed and failed. Make sure that any logic manipulating that image is run from the .load() callback. (As you have nicely done in your above example code.)
If you paste the rest of your page source I can take a look! Good luck!
add load event first then set img'src
because ie run so fast than when you set the src, "load" event was finished
the new load handler will be executed next change
I have a jquery code.
$(window).load(function() {
document.title = $("#myid").text(); //not working in FF
});
Here I have used $(window).load(function() because in the #myid I am getting value through another javascript, if I use ready(), its giving me error. so I am first loading the window then start reading value.
Now in IE, after the window loads itself , I am getting the value of document.title,
but for FF its coming as blank.undefined.
Why? any idea or alternate sln.
It might be a rendering/timing issue.
How are you setting the #myid text? Im assuming you are running this code on page load?
Personaly on another note, i like to use the shorthand version of jQuery DOM ready, this might also fix your problem too.
jQuery(function(){
document.title = jQuery("#myid").text();
});
And i would make sure that you call it at the end of the body or ideally in the head tag.
I think it is possible that firefox triggers ready and load at the same time when it loads quickly (localhost, small experiment page with one div, etc.)
Why not put the title setting in the ready function right after getting it? If You put it in a div, You can put it in the title too.
I didn't check this code and it isn't a good way, but maybe it help you...
If your code isn't working in Firefox only, you can check browser by Javascript and execute my code for Firefox only.
<script type="text/javascript">
var timerId = 0;
function checkElement() {
// If don't work: try .html() or $("#myid").text() != undefined or smth like this
if($("#myid").text()) {
document.title = $("#myid").text();
clearInterval(timerId);
}
}
$(document).ready(function() {
timerId = setInterval('checkElement()', 500);
});
</script>