$("<img/>").attr("src",something).load() not supported in IE < 9? - javascript

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

Related

When is :target computed? [duplicate]

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.

Asynchronous image loading

I'm creating a Hangman game for a project. I have most of the functionality I need, however I am having one issue. When the last incorrect guess is made, it displays the alert box telling you you've lost, before loading the image. I want the image to load first and then the alert box.
I realize that the issue is with the way the DOM loads elements and that I should create a callback function in jQuery.
This is the line in my code that changes the image, and it works fine until it gets to the last one.
document.getElementById("gallows").src = displayGallows[incorrectGuesses - 1];
I have tried using a Jquery function to get this working but my knowledge of jQuery is pretty much non-existent.
var img = $("gallows");
img.load(function() {
alert("Unlucky, you lost. Better luck next time!");
});
img.src = displayGallows[incorrectGuesses - 1];
I have compared this to many posts I have found online and to my untrained eye, it looks OK. When I was troubleshooting I did realize that the img.src was assigned the correct value but the image didn't appear on my page or didn't fire the alert box.
This led me to believe that it may be an issue with linking to the jquery.js file. I have an HTML page that references the jQuery file in it's head tag.
<head>
<title>Hangman</title>
<link rel="stylesheet" href="base.css"/>
<script src="jquery.js"></script>
<script src="hangman.js"></script>
<script src="home.js"></script>
</head>
The file I am writing my JavaScript and jQuery from is the hangman.js file.
Do I also need to refer to the jquery.js file from the hangman.js file? While reading up on this I found that I may have to do this, so I've tried this:
///<reference path="jquery.js" />
and this
var jq = document.createElement('script');
jq.src = 'jquery.js';
document.getElementsByTagName('head')[0].appendChild(jq);
but to no avail, though I don't really understand the second one, as I found these examples on the internet.
Note that in my home.js file I have simple a simple jQuery function to em-bold the text on a button when you mouseover it and this works OK.
If anyone could help me out or point me in the right direction that would be great.
Your best bet here is probably not to use alert at all; instead, use modern techniques like an absolutely-positioned, nicely-styled div to show your message. Showing that won't get in the way of the browser showing the image once it arrives, whereas alert basically brings the browser to a screeching halt until the user clicks the alert away.
But if you want to use alert:
Your concept of waiting for the load event from the image is fine, but you may want to yield back to the browser ever-so-briefly afterward to give it a chance to display the image.
Not using jQuery, as you don't appear to be using it otherwise:
var img = document.getElementById("gallows");
img.onload = img.onerror = function() {
setTimeout(function() {
alert(/*...*/);
}, 30); // 30ms = virtually imperceptible to humans
};
img.src = displayGallows[incorrectGuesses - 1];
That waits for the load or error event from the image, then yields back to the browser for 30ms before showing the alert.
But if you pre-cache the images, you can make the load event fire almost instantaneously. Most browsers will download an image even if it's not being shown, so if you add
<img src="/path/to/incorrect/guess.png" style="display: none">
...to your page for each of the incorrect guess images, then when you assign the same URL to your #gallows image, since the browser has it in cache, it will load almost instantly. Here's an example using your gravatar and a 30ms delay after load: http://jsbin.com/fazera/1
Firstly, to get an object by ID in jQuery, you have to use #.
var img = $("#gallows");
You can not use src or other "vanilla" properties directly on a jQuery object. You can, however do any of these:
Get the actual element from the jQuery object.
var img = $("#gallows");
img.load(function() { ... }
img[0].src = "image.jpg"; // First element in jQuery object
Use the jQuery method attr (recommended).
var img = $("#gallows");
img.load(function() { ... }
img.attr("src", "image.jpg");
Get the element just like you do now.
var img = document.getElementById("gallows");
img.onload = function() { ... }
img.src = "image.jpg";
please make sure that you have your jQuery code that's within the HEAD tag, inside:
$( document ).ready(function() { ...your jQuery here... });
More info: Using jQuery, $( document ).ready()
Your question: "Do I also need to refer to the jquery.js file from the hangman.js file?"
No, but place <script src="hangman.js"></script> tag in the header after referring to your jQuery file:
<head>
<script src="jquery_version_that_you_are_using.js"></script>
<script src="hangman.js"></script>
<script>
$( document ).ready(function() {
//Your jQuery code here
});
</script>
</head>

How to execute code before window.load and after DOM has been loaded?

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.

Javascript: Uncaught TypeError: Cannot call method 'addEventListener' of null

I'm trying to do something fairly simple, but for the reason of me probably not being good enough to search documentation, I can't get this to work.
I have a functioning inline JS that looks like this:
<A title="Wolfram IP Calc" href="javascript:txt=prompt('Enter%20IP%20address,%20e.g.%2010.20.30.40/29','1.2.3.4/5');%20if(txt)%20window.open('http://www.wolframalpha.com/input/?i='+txt);void(O);">Compute!</A>
For various reasons, I'm trying to seperate the JS, and this is where I hit a snag.
I've created the following test page that gives me the error Uncaught TypeError: Cannot call method 'addEventListener' of null:
<HTML> <HEAD profile="http://www.w3.org/2005/10/profile"> <script type="text/javascript">
var compute = document.getElementById('compute');
compute.addEventListener('click', computeThatThing, false);
function computeThatThing() {
txt=prompt('Enter%20IP%20address,%20e.g.%2010.20.30.40/29','1.2.3.4/5');
if(txt) {
window.open('http://www.wolframalpha.com/input/?i='+txt);
}
}
</script></HEAD>
<BODY>
<A title="Wolfram IP Calc" id="compute" href="javascript:void(O);">Test</A>
</BODY>
</HTML>
The only thing I've been able to find that points to a problem like that is that addEventListener can't work with <A> but should handle <IMG> (which suits me fine as I'm going to pour this on some images), so I tried adding the following to no avail:
<img id="compute" src="http://products.wolframalpha.com/images/products/products-wa.png" />
Thanks in advance for pointing out what I'm doing wrong. It is probably glaringly obvious, but I have close to zero experience with JS and I have gone mostly by cargo culting when I've needed it until now.
Your code is in the <head> => runs before the elements are rendered, so document.getElementById('compute'); returns null, as MDN promise...
element = document.getElementById(id);
element is a reference to an Element object, or null if an element with the specified ID is not in the document.
MDN
Solutions:
Put the scripts in the bottom of the page.
Call the attach code in the load event.
Use jQuery library and it's DOM ready event.
What is the jQuery ready event and why is it needed?
(why no just JavaScript's load event):
While JavaScript provides the load event for executing code when a page is rendered, this event does not get triggered until all assets such as images have been completely received. In most cases, the script can be run as soon as the DOM hierarchy has been fully constructed. The handler passed to .ready() is guaranteed to be executed after the DOM is ready, so this is usually the best place to attach all other event handlers...
...
ready docs
Move script tag at the end of BODY instead of HEAD because in current code when the script is computed html element doesn't exist in document.
Since you don't want to you jquery. Use window.onload or document.onload to execute the entire piece of code that you have in current script tag.
window.onload vs document.onload

JavaScript TinyMCE/jQuery race condition on firefox

I have a website with a form that uses TinyMCE; independently, I use jQuery. When I load the form from staging server on Firefox 3 (MacOS X, Linux), TinyMCE doesn't finish loading. There is an error in Firefox console, saying that t.getBody() returned null. t.getBody(), as far as I understand from TinyMCE docs, is a function that returns document's body element to be inspected for some features. Problem doesn't occur when I use Safari, nor when I use Firefox with the same site running from localhost.
Original, failing JavaScript-related code looked like this:
<script type="text/javascript" src="http://static.alfa.foo.pl/json2.js"></script>
<script type="text/javascript" src="http://static.alfa.foo.pl/jquery.js"></script>
<script type="text/javascript" src="http://static.alfa.foo.pl/jquery.ui.js"></script>
<script type="text/javascript" src="http://static.alfa.foo.pl/tiny_mce/tiny_mce.js"></script>
<script type="text/javascript">
tinyMCE.init({ mode:"specific_textareas", editor_selector:"mce", theme:"simple", language:"pl" });
</script>
<script type="text/javascript" src="http://static.alfa.foo.pl/jquery.jeditable.js"></script>
<script type="text/javascript" src="http://static.alfa.foo.pl/jquery.tinymce.js"></script>
<script type="text/javascript" charset="utf-8" src="http://static.alfa.foo.pl/foo.js"></script>
<script type="text/javascript">
$(document).ready(function(){
/* jQuery initialization */ });
</script>
I tried changing script loading order, moving tinyMCE.init() call to the <script/> tag containing $(document).ready() call—before, after, and inside this call. No result. When tinyMCE.init() was called from within $(document).ready() handler, the browser did hang on request—looks like it was too late to call the init function.
Then, after googling a bit about using TinyMCE together with jQuery, I changed tinyMCE.init() call to:
tinyMCE.init({ mode:"none", theme:"simple", language:"pl" });
and added following jQuery call to the $(document).ready() handler:
$(".mce").each( function(i) { tinyMCE.execCommand("mceAddControl",true,this.id); });
Still the same error. But, and here's where things start to look like real voodoo, when I added alert(i); before the tinyMCE.execCommand() call, alerts were given, and TinyMCE textareas were initialized correctly. I figured this can be a matter of delay introduced by waiting for user dismissing the alert, so I introduced a second of delay by changing the call, still within the $(document).ready() handler, to following:
setTimeout('$(".mce").each( function(i) { tinyMCE.execCommand("mceAddControl",true,this.id); });',1000);
With the timeout, TinyMCE textareas initialize correctly, but it's duct taping around the real problem. The problem looks like an evident race condition (especially when I consider that on the same browser, but when server is on localhost, problem doesn't occur). But isn't JavaScript execution single-threaded? Could anybody please enlighten me as to what's going on here, where is the actual problem, and what can I do to have it actually fixed?
The browser executes scripts in the order they're loaded, not written. Your immediate scripts -- tinyMCE.init(...) and $(document.ready(...)); -- can execute before the files finish loading.
So, the problem is probably network latency -- especially with 6 separate scripts (each requiring a different HTTP conversation between the browser and server). So, the browser is probably trying to execute tinyMCE.init() before tiny_mce.js has finished being parsed and tinyMCE is fully defined.
If don't have Firebug, get it. ;)
It has a Net tab that will show you how long it's taking all of your scripts to load.
While you may consider the setTimeout to be duct taping, it's actually a decent solution. Only problem I see is that it assumes 1 second will always fix. A fast connection and they could see the pause. A slow connection and it doesn't wait long enough -- you still get the error.
Alternatively, you might be able to use window.onload -- assuming jQuery isn't already using it. (Can anyone else verify?)
window.onload = function () {
tinyMCE.init(...);
$(document).ready(...);
};
Also, was that a direct copy?
<script type="text/javascript">
$(document).ready(function(){
/* jQuery initialization */ }
</script>
It's missing the ) ending ready:
<script type="text/javascript">
$(document).ready(function(){
/* jQuery initialization */ })
</script>
Missing punctuation can cause plenty of damage. The parser is just going to keep reading until it finds it -- messing up anything in between.
Since this is the first page which came in google when I asked myself the same question, this is what i found about this problem.
source
There's a callback function in tinyMCE which is fired when the component is loaded and ready. you can use it like this :
tinyMCE.init({
...
setup : function(ed) {
ed.onInit.add(function(ed) {
console.log('Editor is loaded: ' + ed.id);
});
}
});
If you are using jquery.tinymce.js then you don't need tiny_mce.js because TinyMCE will try to load it with an ajax request. If you are finding that window.tinymce (or simply tinymce) is undefined then this means that the ajax is not yet complete (which might explain why using setTimeout worked for you). This is the typical order of events:
Load jquery.js with a script tag (or google load).
Load TinyMCE's jQuery plugin, jquery.tinymce.js, with a script tag.
Document ready event fires; this is where you call .tinymce(settings) on your textareas. E.g.
$('textarea').tinymce({ script_url: '/tiny_mce/tiny_mce.js' })
Load tiny_mce.js this step is done for you by TinyMCE's jQuery plugin, but it could happen after the document ready event fires.
Sometimes you might really need to access window.tinymce, here's the safest way to do it:
$(document).tinymce({
'script_url': '/tiny_mce/tiny_mce.js'
'setup': function() {
alert(tinymce);
}
});
TinyMCE will go so far as to create a tinymce.Editor object and execute the setup callback. None of the editor's events are triggered and the editor object created for the document is not added to tinymce.editors.
I also found that TinyMCE's ajax call was interfering with my .ajaxStop functions so I also used a setTimeout:
$(document).tinymce({
'script_url': '/tiny_mce/tiny_mce.js'
'setup': function() {
setTimeout(function () {
$(document).ajaxStart(function(e) {/* stuff /});
$(document).ajaxStop(function(e) {/ stuff */});
}, 0);
}
});

Categories