I have this code:
Function loader() will keep running until <body> does exist. When <body> does exist, insert .loader
<html>
<head>
<script type="text/javascript" src="jquery/jquery-3.2.1.min.js"></script>
<script type="text/javascript">
loader();
function loader() {
if (!$('body').length) {
window.requestAnimationFrame(loader);
} else {
$('<div class="loader"></div>').prependTo('body');
}
};
</script>
</head>
<body>
<!-- elements -->
</body>
</html>
I want to ask this line
if (!$('body').length) {}
What do .length use to determine its return, is it <body> or <body></body>? Does .length wait for closing tag </body> or not? If it does, what is my alternatives? I need .loader to be inserted without waiting for </body>.
My last resort would be
<body>
<div class="container"></div>
<!-- rest of elements -->
</body>
So then
if (!$('.container').length) {}
But I need alternatives that don't interfere with the elements structure.
EDIT:
I rephrase the question and add below codes.
<html>
<head>
<script type="text/javascript" src="jquery/jquery-3.2.1.min.js"></script>
<script type="text/javascript">
$(function(){
console.log("ready timestamp");
});
loader();
function loader() {
if (!$('body').length) {
window.requestAnimationFrame(loader);
} else {
console.log("length timestamp");
}
};
</script>
</head>
<body>
<!-- elements -->
</body>
</html>
Output:
15:03:19.862 length timestamp
15:03:20.198 ready timestamp
Length timestamp always lower than ready timestamp.
Tested with:
Chrome 71.0 and FF 65.0
Enable timestamp in Console Settings (Developer Tools -> Console -> Show timestamps).
Other might ask why I did this, what's right about 300ms?
I implement PWA and need loader, like this:
Showing loader while navigating between pages - PWA.
The problem is, there is 300ms between .on('beforeunload') of the previous page and $() of the next page. It's a blip, obviously.
This blip can ideally/easily be solved by server side scripting but as I mentioned above, I have no access to server side scripting, the furthest I can ask is a blank element <div class="container"></div>.
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script>
console.log( $('#tryToFindMe').length );
</script>
<div id="tryToFindMe"></div>
Similar to how your question tries to access the body before it is defined, this snippet tries to access the div before it is defined. In both cases, the element will not be found.
The reason for this is because when your browser is parsing your webpage into the Document Object Model it parses from top down. So at the point that the script runs, the html that will create the div in the DOM has not been processed yet. So the script tries to lookup the element, it does not exist, and the length is zero.
This is why using the jQuery document ready, binding to the DOMContentLoaded event or the load event exist. These different approaches defer the execution of your logic until either the entire page has been parsed into the DOM, or in the case of load, that not only the page has been parsed into the DOM, but all the resources have been received as well (images, videos, etc). Without these approaches, the script will need to appear in the page after the element is defined to ensure the element exists already in the DOM.
The important part when thinking about interacting with the DOM is not to think in terms of HTML. The DOM contains Nodes, not HTML. The nodes are generated from the HTML.
You can just use a jQuery document ready function ($(() => {})) like so:
$(() => {
$(`<div class="loader"></div>`).prependTo("body");
});
I ran another test.
<html>
<head>
<script type="text/javascript" src="jquery/jquery-3.2.1.min.js"></script>
<script type="text/javascript">
$(function(){
console.log("ready timestamp");
});
loader_bottom();
loader_body();
loader_top();
function loader_bottom() {
if (!$('.container_bottom').length) {
window.requestAnimationFrame(loader_bottom);
} else {
console.log("container_bottom.length timestamp");
}
};
function loader_top() {
if (!$('.container_top').length) {
window.requestAnimationFrame(loader_top);
} else {
console.log("container_top.length timestamp");
}
};
function loader_body() {
if (!$('body').length) {
window.requestAnimationFrame(loader_body);
} else {
console.log("body.length timestamp");
}
};
</script>
</head>
<body>
<div class="container_top"></div>
<!-- elements between -->
<div class="container_bottom"></div>
</body>
</html>
Output:
09:09:16.894 body.length timestamp
09:09:16.897 container_top.length timestamp
09:09:17.090 container_bottom.length timestamp
09:09:17.220 ready timestamp
Because $('.container_top') adjacent to $('body'), changing the sequence,
loader_top();
loader_body();
Not suprisingly output:
09:09:39.708 container_top.length timestamp
09:09:39.708 body.length timestamp
Hence I come into conclusions:
Does jQuery .length defies DOM ready? Yes it does.
What do .length use to determine its return, is it <body> or <body></body>? It is <body>.
Although the right phrase for second question should be:
What DOM Parser use to determine nodes, is it <tag> or <tag></tag>? The answer is <tag>.
Actually I'm not surprised that we can manipulate DOM before it's ready. Besides, $().ready() is just an event when DOM is ready. Javascript is multi-thread so (I thought) DOM Manipulating should could run parallel with DOM Parsing. Not until this code I can prove that.
Related
I really don't know why this isn't working. In my HTML, if I put my script in the head section, I get the following error in my console:
Uncaught TypeError: Cannot read property 'addEventListener' of null
If I put my script in the bottom part of the body, it's working fine.
HTML :
<head>
<link rel="stylesheet" type="text/css" href="style.css">
<script type="text/javascript" src="script.js"> </script>
</head>
<body>
<ul>
<li> Shahin </li>
<li> Lina </li>
<li> Adrita </li>
</ul>
<img src="./img/1.jpg" class="hidden">
<img src="./img/2.png" class="hidden">
<img src="./img/3.jpg" class="hidden">
</body>
JavaScript :
var shahin = document.getElementById('img1');
var lina = document.getElementById('img2');
var adrita = document.getElementById("img3");
shahin.addEventListener('click',picShow);
lina.addEventListener('click',picShow);
adrita.addEventListener('click',picShow);
function picShow() {
console.log(this);
}
Can anybody tell me what I'm doing wrong and where is the proper place to put the script tag? Also, what change should I make to run my script from head section? I will be glad for your answer. Thanks in advance
Your script is loading and being executed prior to the rest of the body HTML, so naturally, document.getElementById is going to return null.
You should be running your event listener subscription code on or after the document.ready event.
Wrap the code you posted into a JS function, then set that function as the callback to be executed on document.ready:
document.addEventListener('load', initFn);
<script type="text/javascript" defer src="script.js"> </script>
Add key word defer, script runs when all DOM elements will loaded
If you put your <script> in the <head> javascript loading from top to bottom. So when loading it returns undefined eventlistener. So try use the script just before the end of your </body> tag and it will works.
Like so:
<script type="text/javascript" src="script.js"> </script>
</body>
Further to Josh E's answer:
You should be running your event listener subscription code on or after the document.ready event
If you want to execute your code as soon as the element is available, you can use requestAnimationFrame to scan the DOM during page load instead of waiting for the entire page to load:
function doSomethingWithElement(myElement) {
myElement.addEventListener('click', function() {
// Do stuff here
});
}
function searchForElement() {
const el = document.getElementById('img1');
if (el) {
return doSomethingWithEl(el);
}
window.searchID = window.requestAnimationFrame(searchForElement);
}
// Store the request ID so it can be cancelled later
// Otherwise it'll run forever!
window.searchID = window.requestAnimationFrame(searchForElement);
window.addEventListener('load', function() {
window.cancelAnimationFrame(window.searchID);
});
This might be overkill but it's super useful when you want to manipulate or add listeners to DOM elements as soon as is possible.
I'm writing a small script that determines if the user is on IE8 or below. If they are, the script should completely empty the document (body and head) and stop any further script executing.
I've played around with document.write() but can only get this working with window.onload. But I want it to execute as soon as it knows the browser version (which is when the script executes).
Example page setup:
<html>
<header>
Some CSS
Some meta
...
</head>
<body>
Page content
<script>
if (IE < 8) { //in reality I have a function to determine this
document.write('You browser is outdate. Please upgrade to view this site.');
}
</script>
<script src="more-scripts"></script>
</body>
</html>
This doesn't work but if I wrap the script in a window.onload it does. But then the page flashes up before the code executes. How can I get this to work?
Rather than using document.write() to print a message, you can use the .innerHTML property of the document.body element to entirely replace the body of the page. For this technique, your browser-check script should go in the head section, not the body (this is usually where scripts like this would go anyway).
<html>
<header>
Some CSS
Some meta
...
<script>
if (IE < 8) { //in reality I have a function to determine this
document.body.innerHTML = "You browser is outdate. Please upgrade to view this site.";
}
</script>
</head>
<body>
Page content
<script src="more-scripts"></script>
</body>
</html>
you could use conditional comments for that:
<!--[if IE 8]>
<script>
document.body.innerHTML = '';
document.write('You browser is outdate. Please upgrade to view this site.');
</script>
<![endif]-->
When i keep my javascript/jquery external, my code doesn't work. but when i combine them in my html file everything is fine.
any suggestions as to why this is?
here is the code:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<script type ="text/javascript" src="jquery.js"></script>
<script type ="text/javascript" src="program.js"></script>
</head>
<body>
<div id="clickme">
Click here
</div>
<img id="book" src="book.png" alt="" width="100" height="123" />
<p>First Paragraph</p>
<p>Second Paragraph</p>
<p>Yet one more Paragraph</p>
</body>
</html>
with external javascript
$('#clickme').click(function() {
$('#book').fadeOut('slow', function() {
// Animation complete.
});
});
$("p").click(function () {
$(this).slideUp();
});
VERSUS
<!DOCTYPE html>
<html>
<head>
<script type ="text/javascript" src="jquery.js"></script>
</head>
<body>
<div id="clickme">
Click here
</div>
<img id="book" src="book.png" alt="" width="100" height="123" />
<p>First Paragraph</p>
<p>Second Paragraph</p>
<p>Yet one more Paragraph</p>
<script>
$('#clickme').click(function() {
$('#book').fadeOut('slow', function() {
// Animation complete.
});
});
$("p").click(function () {
$(this).slideUp();
});
</script>
</body>
</html>
I guess you execute the click event before the DOM finishes loading. Wrap your code inside the dom ready event and it should work, Assuming your path to the external javascript file is correct.
$(function(){
$('#clickme').click(function() {
$('#book').fadeOut('slow', function() {
// Animation complete.
});
});
$("p").click(function () {
$(this).slideUp();
});
});
Always use firebug (console) to see what is wrong with the script, if you run into any script errors.
Your javascript is executed before there are elements on the page. You can get around this by using $(document).ready(function(){...}); or moving your external javascript files to the bottom.
Wrap your js code in external file in
$(document).ready(function(){
//your code goes here
});
Right now you are including external js file in header and it is executed. At this point there is no elements so $('#clickme') and $("p") are empty set. In the second example you run this code after rendering html with that elements.
The reason that there is a difference, is that in the external file your code is executing before the browser has fully parsed the DOM so you are attempting to programatically access elements of the page which the browser is not yet aware of. This is exactly what most people have already said, but let me elaborate a bit further...
Whilst a lot of people have mentioned using jQuery's document ready handler, I would like to point out that a workable solution is simply to move your script tags to the bottom of the page.
Not only will this solve your problem in itself, but it will also improve page load times because of how browsers treat scripts. When the browser encounters a script it stops everything else it is doing (known as a "blocking" operation), and parses and executes the script. This causes the page to just appear to stall from a user's perspective, meaning a bad user experience. Thus, because the scripts are parsed and executed only as they are encountered, by moving your scripts to the bottom you allow the browser to fully render the page so that the JavaScript does not block rendering.
Though rather than just moving scripts to the bottom of the page, I'd also follow what the others recommended and wrap the whole code in the document ready handler just to be extra safe that your code will always be executed at the correct time.
Also, in the debate of inline or external, external scripts are generally preferred as they are easier to maintain and the browser can cache them independently of the page (providing the correct HTTP headers are present).
To sum up here's some example code:
<html>
<head></head>
<body>
<!-- all your markup here -->
<!-- script at bottom, markup already rendered by this point -->
<script type="text/javascript" src="jquery.js"></script>
<!-- inline or external, still wrap in document ready handler -->
<!-- though external is better because the browser can cache it independently of the page -->
<script type="text/javascript">
//wrap in document ready to be extra safe
$(function() { /*code here*/ });
</script>
</html>
i am using this script in my jsp. some time when i click on that particular button it works fine but some time it doesn't work and show Script Error : document.form is null not an object. what ever i searched i found that document is not finished loading when i call reset. how can i check whether the document has loaded or not?
<head>
<script type="text/javascript">
function closeWarning(){
document.forms[0].reset();
}
</script>
</head>
<body onLoad="closeWarning();"
<jsp:include flush="true" page="/myCart/header.jsp"/>
<div>
// content of body
</div>
</body>
If you don't want to use jQuery, you can use an event listener for DOMContentLoaded, as in:
if(document.addEventListener) document.addEventListener("DOMContentLoaded",closeWarning,false);
This will work for everyone except IE, which uses onreadystatechanged, as in:
document.onreadystatechange=function() { if(this.readyState=="complete") { closeWarning();
}
}
One way is using jQuery and it's ready() function.
I am trying to use jQuery's .load function to dynamically load content into my webpage. This seem so simple, but I cannot make it work. To try and figure it out, I made a test page with just basic structure, but the external content still won't load:
jquery.html
<html>
<head>
<title>JQuery Test</title>
<script src="jquery1.5.min.js"></script>
</head>
<body>
<script>
$('#foo').load('test.html');
</script>
<div id="foo"></div>
</body>
</html>
test.html
<p>Text text</p>
I'm sure I have made a tiny error, but I can't find it anywhere!
You need to encapsulate your script in the $(document).ready() otherwise #foo won't exist when the script is executed:
<script>
$(document).ready(function(){
$('#foo').load('test.html');
});
</script>
You need to wait for the document to be ready before you can access the DOM. Just add a $(document).ready() around your original code:
<html>
<head>
<title>JQuery Test</title>
<script src="jquery1.5.min.js"></script>
</head>
<body>
<script>
$(document).ready( function() {
$('#foo').load('test.html');
});
</script>
<div id="foo"></div>
</body>
</html>
or if you want a shorter code:
$(function() {
$('#foo').load('test.html');
});
Informally, what's happening is that, as your browser reads the code you wrote, it's drawing its contents as it goes along. When it reaches your <script> tag, it executes it. But when $("#foo") gets executed, the browser's still processing the <script> and hasn't reached the part of the code where you told it there's a div called foo, so the browser doesn't know it exists, and jquery will just find nothing.
Of course, the idea that the browser will just sequentially read your code and render it as it goes is naive at best, so while it might seem that just moving the <script> tag to the bottom of the code would work, you're not actually guaranteed it will work. Instead, the browser will notify you when it's done drawing the page by firing a load (and possibly a DOMContentLoaded) event. So all code that depends on the whole html being drawn should be executed in an event handler bound to those events.
jQuery makes waiting for the page to be loaded easy, just use something like this:
$.ready(function() {
doStuff();
});