jQuery not getting called in all browsers - javascript

Disclaimer: I am new to jQuery.
I am trying to implement a fadeOut effect in jQuery for a div block, and then fadeIn effect on two other div blocks.
However, these effects are only working in the Chrome browser (i.e. they won't work in Safari, FireFox, Opera) which is rather perplexing to me. I have tried clearing my cache in case it was storing an old file, but none of that seemed to do anything.
Basic idea (stored in mainsite.js file):
$("#videoThumbnail_XYZ").click(function () {
$("#thumbnailDescription_XYZ").fadeOut(300);
$("#videoPlayer_XYZ").delay(300).fadeIn(100);
$("#videoHiddenOptions_XYZ").delay(300).fadeIn(100);
});
So when a div tag with the id of videoThumbnail_XYZ is clicked, it starts the fadeOut and fadeIn calls on the other div tags.
I am loading my javascript files into the page in this order (so jQuery is loaded first):
<script src="http://code.jquery.com/jquery-1.4.4.js"></script>
<script async="" type="text/javascript" src="javascripts/mainsite.js"></script>
Any guidance you could give is greatly appreciated!

Make sure the DOM is fully loaded before your code runs.
A common way of doing this when using jQuery is to wrap your code like this.
$(function() {
$("#videoThumbnail_XYZ").click(function () {
$("#thumbnailDescription_XYZ").fadeOut(300);
$("#videoPlayer_XYZ").delay(300).fadeIn(100);
$("#videoHiddenOptions_XYZ").delay(300).fadeIn(100);
});
});
This is a shortcut for wrapping your code in a .ready() handler, which ensure that the DOM is loaded before your code runs.
If you don't use some means of ensuring that the DOM is loaded, then the #videoThumbnail_XYZ element may not exist when you try to select it.
Another approach would be to place your javascript code after your content, but inside the closing </body> tag.
<!DOCTYPE html>
<html>
<head><title>your title</title></head>
<body>
<!-- your other content -->
<script src="http://code.jquery.com/jquery-1.4.4.js"></script>
<script async="" type="text/javascript" src="javascripts/mainsite.js"></script>
</body>
</html>

If mainsite.js is being included before your div is rendered, that might be throwing the browsers for a loop. Try wrapping this around your click handler setup:
$(document).ready(function(){
// your function here
});
That'll make sure that isn't run before the DOM is ready.
Also, you might consider putting the fadeIn calls in the callback function of your fadeOut, so if you decide to change the duration later on, you only have to change it in one place.
The way that'd look is like this:
$("#thumbnailDescription_XYZ").fadeOut(300,function(){
$("#videoPlayer_XYZ").fadeIn(100);
$("#videoHiddenOptions_XYZ").fadeIn(100);
});

I see you have a delay set to the same duration your fadeOut is, I would recommend instead of delaying which in essence your waiting for the animation to complete that instead you use the callback function.
$("#videoThumbnail_XYZ").click(function () {
$("#thumbnailDescription_XYZ").fadeOut(300, function() {
$("#videoPlayer_XYZ").fadeIn(100);
$("#videoHiddenOptions_XYZ").fadeIn(100);
});
});

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 and run other jQuery code.
$(document).ready(function(){
$("#videoThumbnail_XYZ").click(function () {
$("#thumbnailDescription_XYZ").fadeOut(300);
$("#videoPlayer_XYZ").delay(300).fadeIn(100);
$("#videoHiddenOptions_XYZ").delay(300).fadeIn(100);
});
});
All three of the following syntaxes are equivalent:
* $(document).ready(handler)
* $().ready(handler) (this is not recommended)
* $(handler)

Related

Javascript: Set eventListener to DOM object without window.onload?

My index.html include two javascript codes which are plugin.js and main.js(main include after plugin),
I want to make plugin.js as a reusable plugin, like checking if the index.html file has any elements which have click-me class attribute, if index.html has this kind of elements, then set every this kind of elements an eventListener.
I want my window.onload function ALWAYS in main.js rather than plugin.js, but in this case I cant' get DOM object by className in plugin.js, I also don't want to call any function of plugin.js in main.js.
Does anyone ahs any idea?
You can just include the scripts after the body.
<body>
</body>
<!-- add your scripts here -->
Then you don't need to check if the document is ready anymore, this has the drawback of only starting the download after the page is fully rendered.
Another possibility is using defer
<script type="text/javascript" src="plugin.js" defer></script>
<script type="text/javascript" src="main.js" defer></script>
This way scripts are downloaded as soon as possible but are executed only after the page is ready, and in order.
A more detailed answer here
Well, you should properly use onload as an event listener:
window.addEventListener("load", function() {
});
This can be added as many times as you wish, both in main.js and plugin.js.
Additionally, it's better to use DOMContentLoaded event, because it doesn't wait on loading images. That's really important, if you rely on window.onload, just one pending image can make your page useless for first 10 seconds of the visit. It's used like this:
document.addEventListener("DOMContentLoaded", function(event) {
console.log("DOM fully loaded and parsed");
});
It has compatibility issues however, so I recommend using some library.
Oh and last thing - don't link your scripts before body unless needed. They block parsing of the page.
Why don't you just check if the clicked element have the click-me class from the root? It makes you declare only one listener on the body.
function listener(event) {
if(event.target.classList.contains("click-me")) {
// Do something
}
}
document.body.addEventListener("click", listener, true);

What javascript should put inside $(document).ready(function()?

Does $(document).ready(function() { means all javascript file has been downloaded so any js init or func should work?
so it is a good practise to always put js inside $(document).ready(function() { ?
$(document).ready is part of the page lifecycle and runs after all of the resources have been loaded for the page (HTML, CSS and JS files).
You should be functions in here that you need to run when the page first loads, so generally initialization of plugins, first run functions and attaching events to elements.
Any other functions that can run after the page has loaded can be defined outside of this scope.
Note that if you are dynamically inserting DOM elements, any events attached to that type or class (for example) will not be attached to them without re-attaching, or using .on and attaching to the document itself.
Use ready() to make a function available after the document is loaded:
for eg:
$(document).ready(function(){
$("button").click(function(){
$("p").slideToggle();
});
});
Definition and Usage
The ready event occurs when the DOM (document object model) has been loaded.
Because this event occurs after the document is ready, it is a good place to have all other jQuery events and functions. Like in the example above.
The ready() method specifies what happens when a ready event occurs.
note: The ready() method should not be used together with <body onload="">.
From jQuery documentation:
Description: Specify a function to execute when the DOM is fully loaded.
It means that you can specify a function to run after the DOM is fully loaded, ie is available to interact with javascript.
Following is copied from here
$(document).ready(...)
Fire when all DOM loaded (even if multimedia no loaded yet)
$(window).load(...)
Fire when all content loaded (progress indicator which shows loading proccess) gone.
Now here is my suggestion (not from that link)
I think the better approach would be putting your script tags at the end of body, like this, as it makes sure that the script is loaded when everything else has been loaded
<html>
<head></head>
<body>
<div>
bla bla bla
</div>
<script src="1"></script>
.
.
<script src="n"></script>
<script>console.log('hello');</script>
</body>
</html>

Why I need to use `setTimeout()` to make `jquery.animate` work in a backbone view?

I want to invoke jquery.animate directly to change the effect of a div, but found it doesn't have any effect.
Instead, I need to put it inside a setTimeout(..., 0) to make it work.
I wonder why do I need to do this, and is it the best approach?
Live demo
http://jsbin.com/docahu/2/edit
Or here:
var FooView = Backbone.View.extend({
id: 'foo',
});
var BarView = Backbone.View.extend({
render: function() {
$("#foo").animate({width: '200px'});
// !!! HERE !!!
setTimeout(function() {
$("#foo").animate({height: '100px'});
}, 0);
return this;
}
});
var fooView = new FooView();
var barView = new BarView();
var combinedView = $(fooView.render().el).append(barView.render().el);
$(document.body).append(combinedView);
#foo {
width: 50px;
height: 50px;
background-color: blue;
color: white;
}
<!DOCTYPE html>
<html>
<head>
<meta name="description" content="Jquery animate delay problem in backbone render" />
<script src="//code.jquery.com/jquery-1.9.1.min.js"></script>
<script src="//jashkenas.github.io/underscore/underscore-min.js"></script>
<script src="//jashkenas.github.io/backbone/backbone-min.js"></script>
<meta charset="utf-8">
<title>JS Bin</title>
</head>
<body>
</body>
</html>
You can see height is changed but the width is not.
PS:
Also I found $(document).ready() is also working:
$(document).ready(function() {
$("#foo").animate({height: '100px'});
});
Which one is better to use?
It's because it's trying to animate the width before the element is in the DOM. If you put a selector in that position, you'll probably find it's not getting anything.
Doing a timeout of 0 (so javascript finishes rendering the things THEN tries the animation) or waiting for the document to finish rendering fixes that
Things happen in this order:
You render your view, but it's unattached to the DOM
the width animation runs. Nothing happens because '#foo' isn't on the DOM.
you attach it to the dom.
your height animation runs. It works because '#foo' is in the DOM.
Well seems like it works by chance. The reason the first one is not working is probably because the object are still not loaded on the screen. The second one is working because after the timer was dispatched and ended (this does not really take 0 time) the page was loaded by another thread on the computer. So the overhead of creating the timer and calling back the procedure is apparently enough to finish loading the page.
You should use $(document).ready to make sure it is always called after the document is fully loaded, because like I said, it is now working by chance, and a different browser\machine may not run any of the two (or both).
Background: JavaScript starts getting executed while the page is loaded, and the DOM is build at the same time (just at the time the HTML and JavaScript text is downloaded). So if you reference DOM objects from JavaScript code like you are doing now, you get a race condition where the outcome is not defined. To avoid that there is the $(document).ready callback.
Edit
See this question. Also the Udacity course is really cool to understand what is going on.
Updated
TL;DR
Using $(document).ready is equivalent to placing the JavaScript at the end of the document…
JSBin (which is were the OP posted his sample code) will execute the JavaScript after all the HTML elements render, but before the $(document).ready event. Binding the jQuery.animate in $(document).ready is the same as firing it anytime after the view is appended to the DOM.
The simple and stable solution is to simply invoke
$("#foo").animate({width: '200px'});
on the last line of the OP's code. (Read the end of the Answer to see a more formal way of binding the jQuery.animate function)
To answer the OP's original quesiton: setTimeout() works in your case because of the way async functions are queue in the JavaScript runtime. If the <body> has already been loaded, then using setTimeout(0) the way the OP does, will have the same effect as placing the animation binding in $(document).ready.
Why setTimeout(0) works
The first thing to understand is that JavaScript is not a multi-threaded framework. While you certainly can invoke non-synchronous functions, asynchronous, async functions don't actually run parallel to the synchronous operations. Instead, async functions are queued to run as soon as the runtime is free.
For example, take the following three synchronous functions.
function1();
function2();
function3();
As you'd expect function1will fire first, followed, in order, byfunction1, function2thenfunction3. However, if I place function1` in an asynchronous call
setTimeout(function1,0);
function2();
function3();
then function1 will be placed on a queue, leaving function2 and then function3 to fire. As soon as the event loop is finished function1 is invoked. That is, it fires last! You can see this in action in this fiddle.
In the OP's example, setTimeout(function() { $("#foo").animate({height: '100px'});}, 0); was fired immediately after the runtime executed $(document.body).append(combinedView); and so jQuery was able to find the #foo element, so technically this is a correct way to do what the OP wants. This is true because of the way JSBin works. That is, it loads the JavaScript from its JavaScript Module after the DOM has loaded (but before the $(document).ready event).
Edited:
Do not use the $(document).ready function...in general
I think there's some confusion regarding how $(document).ready function solves the OP's problem. Most of the confusion probably stems from the complexity of how different web page elements (HTML, CSS, JavaScript) affect the rendering of the DOM.
There is a main parsing and rendering thread used by browsers. This is where your HTML is processed, your CSS stylesheets are fetched and parsed, and your JavaScript is fetched/parsed. All of these operations are executed as they are encountered and will be blocking (unless async or defer is specified in your <link>/<script> tags).
The order in which you pace all of these tags is essential. If your script tag is written at the top of your document (say within the <head> tag) it will be executed before any HTML is injected into the DOM.
In essence, using $(document).ready is equivalent to placing the JavaScript at the end of the document… Since JSBin (which is were the OP posted his sample code) will execute the JavaScript after all the HTML elements render, but before the $(document).ready function, binding the jQuery.animate in $(document).ready is the same as firing it anytime after the view is appended to the DOM.
Instead, the simple and stable solution is to simply invoke
$("#foo").animate({width: '200px'});
after both the fooView and the barView have been attached to the DOM. Or more formally:
var BarView = Backbone.View.extend({
render: function() {
// process your html here
return this;
}
bindTransitions: function {
$("#foo").animate({width: '200px', height: '100px'});
}
});
var fooView = new FooView();
var barView = new BarView();
var combinedView = $(fooView.render().el).append(barView.render().el);
$(document.body).append(combinedView);
barView.bindTransitions();
If you've properly scoped your backbone view, you should be able to reference the element that is currently in memory when you are trying to change the width or height (pre-render).
You can do this by doing something similar to: this.$el.find(#foo") to obtain access / manipulate to your markup before it is added to the DOM.

$(document).ready() fires too early

So, I need to know the width of an element with javascript, the problem I have is that the function fires too early and the width changes when the css is tottally applied. As I understood, the $(document).ready() function was fired when the document is completed, but it doesn't seem to work like that.
Anyways, I'm sure that with the code my problem will be understood (this is a simplified example):
<html>
<head>
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script>
<link href='http://fonts.googleapis.com/css?family=Parisienne' rel='stylesheet' type='text/css'>
<style type="text/css">
#target {
font-family: 'Parisienne', cursive;
float: left;
}
</style>
</head>
<body>
<div id="target">Element</div>
</body>
</html>
<script type="text/javascript">
$(document).ready(function(){
console.debug($('#target').outerWidth());
alert('hold on');
console.debug($('#target').outerWidth());
});
</script>
I want to know the width of the #target div, the problem is that the code that's executed before the alert gives a different output than the one after, presumably because the font is not fully loaded and it's measuring the div with the default font.
It works as I expect in Google Chrome, but it doesn't on IE and Firefox.
If you rely on external content to be already loaded (e.g. images, fonts), you need to use the window.load event
$(window).on("load", function() {
// code here
});
The behaviour of these events is described in this article:
There is [a] ready-state however known as DOM-ready. This is when the browser has actually constructed the page but still may need to grab a few images or flash files.
Edit: changed syntax to also work with jQuery 3.0, as noted by Alex H
Quote OP:
"As I understood, the $(document).ready() function was fired when the document is completed,"
$(document).ready() fires when the DOM ("document object model") is fully loaded and ready to be manipulated. The DOM is not the same as the "document".
W3C - DOM Frequently Asked Questions
You can try $(window).load() function instead...
$(window).load(function() {
// your code
});
It will wait for all the page's assets (like images and fonts, etc.) to fully load before firing.
The jQuery .ready() function fires as soon as the DOM is complete. That doesn't mean that all assets (like images, CSS etc) have been loaded at that moment and hence the size of elements are subject to change.
Use $(window).load() if you need the size of an element.
The "ready" event fires when the DOM is loaded which means when it is possible to safely work with the markup.
To wait for all assets to be loaded (css, images, external javascript...), you'd rather use the load event.
$(window).load(function() {
...
});
You could use $(window).load(), but that will wait for all resources (eg, images, etc). If you only want to wait for the font to be loaded, you could try something like this:
<script type="text/javascript">
var isFontLoaded = false;
var isDocumentReady = false;
$("link[href*=fonts.googleapis.com]").load(function () {
isFontLoaded = true;
if (isDocumentReady) {
init();
}
});
$(document).ready(function () {
isDocumentReady = true;
if (isFontLoaded) {
init();
}
});
function init () {
// do something with $('#target').outerWidth()
}
</script>
Disclaimer: I'm not totally sure this will work. The <link> onload event may fire as soon as the stylesheet is parsed, but before its external resources have been downloaded. Maybe you could add a hidden <img src="fontFile.eot" /> and put your onload handler on the image instead.
I have absolutely, repeatably seen the same problem in IE9 and IE10. The jquery ready() call fires and one of my <div>'s does not exist. If I detect that and then call again after a brief timeout() it works fine.
My solution, just to be safe, was two-fold:
Append a <script>window.fullyLoaded = true;</script> at the end of the document then check for that variable in the ready() callback, AND
Check if $('#lastElementInTheDocument').length > 0
Yes, I recognize that these are nasty hacks. However, when ready() isn't working as expected some kind of work-around is needed!
As an aside, the "correct" solution probably involves setting $.holdReady in the header, and clearing it at the end of the document. But of course, the really-correct solution is for ready() to work.
The problem $(document).ready() fires too early can happen sometimes because you've declared the jQuery onReady function improperly.
If having problems, make sure your function is declared exactly like so:
$(document).ready(function()
{
// put your code here for what you want to do when the page loads.
});
For example, if you've forgotten the anonymous function part, the code will still run, but it will run "out of order".
console.log('1');
$(document).ready()
{
console.log('3');
}
console.log('2');
this will output
1
3
2

Jquery event handling code doesnt work after including script in head from an external js file

I included a script into my document's head that contains the following jquery code:
$('.unappreciatedIcon').click(function() {
alert('JS Works!');
});
In the body of my document I have following:-
<span class="unappreciatedIcon">.....</span>
But there is no alert displayed when I inserted the script into the document head from an external js file. When I had put this script in body simply below the target elements this worked flawlessly.
Thanks to you all:
I am getting this to work with the following code:
$(document).ready(function(){
$('.unappreciatedIcon').click(function() {
alert('fds');
})
});
Did you wrap your jquery in a $(document).ready(function() { // your code // }); ?
If not your jquery code is executing immediately and the browser has not loaded your span. You need to wait for the document to be ready (using the code above) before assigning events.
Update
$(document).ready(function() {
$('.unappreciatedIcon').click(function() {
alert('JS Works!');
});
});
When your script ran, it looked for an element having the class unappreciatedIcon. Nothing was found because the document is still being parsed and there was no node having the class unappreciatedIcon available in the document so far. The DOM is being constructed incrementally.
But when you put your script after the span element occurs, then $('.unappreciatedIcon') was found because it has been parsed and added to the DOM, so the click handler was tied to it.
Either run your code in a ready callback. The ready callback basically runs when the entire HTML has been parsed and the DOM is fully constructed which is usually a safe point to start running your JavaScript code that depends on the DOM.
$(document).ready(function() {
$('.unappreciatedIcon').click(...)
});
or put your code after the element occurs (don't need to wrap it inside the ready callback in this case),
<span class="someClass">..</span>
..
<script>
$('.unappreciatedIcon').click(...)
</script>
just going to go with basics but did you make sure to include the jquery library? If it doesn't work and it's in the code you can also open in firefox with firebug go to the console tab and see what error you have.
The javascript is being processed before the page has finished rendering. As Erik Philips suggested, you need to put this statement inside your $(document).ready() function to ensure the page is loaded before the statement is evaluated.
$(document).ready(function(){
$('.unappreciatedIcon').click(function() {
alert('JS Works!');
});
});
here is the fiddle http://jsfiddle.net/Pf4qp/
Since HTML loads from top to bottom, the head loads before the rest of the page. You could solve this problem by putting the link to your js file right before the end tag. However, its generally better practice to put the javascript link in the head.
A better alternative is to use the defer attribute in the script tag.
For example:
<script type="text/javascript" src="script.js" defer></script>
or
<script type="text/javascript" src="script.js" defer="defer"></script>
The second option is kind of unneccessary though. This attribute is pretty well supported. Internet Explorer has supported it since version 5.5 though apparently it is "buggy" through IE9. It has been fully supported since FireFox 3.5, Chrome 8.0, Safari 5.0. It also works with all current mobile browsers. I guess it is not supported by any Opera browsers though.

Categories

Resources