Scope error in JavaScript - javascript

I was playing with input[range] in html with javascript. I came up with a problem. Here is the code:-
<html>
<head>
<meta charset="utf-8">
<title>Slider and range !!</title>
<script type="text/javascript">
let div = document.getElementById('display');
function foo(value) {
div.innerHTML = value;
}
</script>
<style>
input[type='range']{
border : 5px grey solid;
border-radius: 10px;
}
</style>
</head>
<body>
<input id="input" type="range" min = '0' max ='100' step="10" oninput="foo(this.value)" >
<div id="display"></div>
</body>
</html>
Here in webconsole div is shown undefined but if I declare the function as this:-
function foo(value) {
let div = document.getElementById('display');
div.innerHTML = value;
}
The code is working.Why is this working like this way?Please help Thank you.

Let's understand the how DOM parsing works on Browser.
When we load any document/HTML on DOM/Browser, It starts document parsing. It iterated elements and creates a DOM tree. It starts from TOP, goes to the header and check for other resources and execute them one by one.
Here when you wrote
let div = document.getElementById('display');
in simple script tag without wrapping it into any function, it executed first without getting your DOM loaded.
Which means, the parser is looking for DIV by its ID which is not present yet on the browser. Hence it is undefined. Now the question is how does it work inside the function?
Functions in JavaScripts gets executed on DOMContentLoaded. This is similar to jQuery ready(). This ensures that the DOM has been ready and each element on the browser has been traversed and is now in DOM tree.
Now, you probably thinking how it works when we put this code at the bottom of the page? like
<html>
<head></head>
<body>
YOUR HTML
</body>
<script type="text/javascript">
//YOUR JS CODE
</script>
So when we insert JS which is looking for an element in DOM at the top of the page in head tag and if there is an error in getting the JS executed (like in your case the JS is looking for DIV which is not on DOM yet) it will stop the execution of DOM unless the script resolved the error.
Hence when we place JS code at the bottom of the page, the browser has already parsed all the page element and the DOM tree is ready. Thus, any element is now accessible by JS.
Which means, if you write your above code at the bottom of the page, it will get the element without error.
Footnote:
Read more about how JavaScript works on browser
https://javascript.info/onload-ondomcontentloaded
Where should I put <script> tags in HTML markup?
Hope this helps,
Thanks
-AG

check this, give the script at the bottom of the page,
means initialize the div variable, only after rendering the page
<html>
<head>
<meta charset="utf-8">
<title>Slider and range !!</title>
<script type="text/javascript">
let div;
function onLoadFunction() {
div = document.getElementById('display');
}
function foo(value) {
div.innerHTML = value;
}
</script>
<style>
input[type='range'] {
border: 5px grey solid;
border-radius: 10px;
}
</style>
</head>
<body onLoad="onLoadFunction()">
<input id="input" type="range" min='0' max='100' step="10" oninput="foo(this.value)">
<div id="display"></div>
</body>
</html>

You try to access the display element before it is loaded. Either you will have to place your script lower in the HTML file or make it execute at the onLoad function of the document.
document.onload = function() {
//run your code here
}

Not working first approach because your trying access the DOM element which not loaded yet. let div = document.getElementById('display'); by this code you trying to access immediately. When the JS code in global scope it is parse line by line and all lines execute immediately.
But following code is working fine because here you accessing the DOM element in function scope and function only running when range value are changing in the meantime the DOM is already loaded and available to manipulate.
function foo(value) {
let div = document.getElementById('display'); // Now `display` available, so no error
div.innerHTML = value;
}

Related

document.getElementById('blocks').value = blocks; is giving an error

Okay so, I once tried this a long time ago and it never happened before.
if I do
var blocks = 0;
document.getElementById('blocks').value = blocks;
Uncaught TypeError: Cannot set property 'value' of null
<!DOCTYPE html>
<html>
<head>
<title>Home</title>
<link rel="stylesheet" href="/css/Index.css">
<script src="/js/Index.js"></script>
</head>
<body>
<input type = "text" id="blocks"/>
</body>
</html>
Try moving your script to the bottom of the body. Element does not exist at the time of execution.
<head>
<title>Home</title>
<link rel="stylesheet" href="/css/Index.css">
</head>
<body>
<input type = "text" id="blocks"/>
<script src="/js/Index.js"></script><!-- here-->
</body>
If your script is saved in its own JS file (i.e. not as a <script> tag in the HTML), you cannot access DOM elements until the document is loaded. So, wrap your code in an onload event handler:
document.onload = function() {
var blocks = 0;
document.getElementById('blocks').value = blocks;
}
Try placing the <script> tag at the end of your <body> element to ensure that the element is accessible at the time the script is called:
<!DOCTYPE html>
<head>
<title>Home</title>
<link rel="stylesheet" href="/css/Index.css">
</head>
<body>
<input type = "text" id="blocks"/>
<script src="/js/Index.js"></script>
</body>
load your script in footer section or very end of the body instead of head. Your script is executing before DOM is loaded or call the specified statements under load event.
This is failing because your script is running before the DOM is finished rendering. You should always place your DOM-manipulating script inside document.onload function, which runs after the DOM has finished loading.
Also, you should always check for NPE (NullPointerException); it's a good habit to get into:
document.onload = () => {
const element = document.getElementById('blocks');
if (element) {
element.value = 0;
} else {
// consider publishing some warning
console.error("Could not find element #blocks");
}
}
As others have suggested, the common practice to ensure your script runs after elements have loaded is to move the script tag to the bottom of the body, after the elements themselves. However, a more modern approach is to use the defer attribute on your script tag: this ensures the script won't load until your DOM has loaded, but you don't need to worry about the tag's placement in the page relative to DOM elements you'll be manipulating. It's supported by all modern browsers, even IE10+.
So simply add defer onto your existing <script> tag and everything should work :)

How to alert all content of page on button click with JavaScript

I need to get all the content of page including all codes on JavaScript alert. Please check the code.
function getContent() {
var content = document.getElementsByTagName('html').value;
alert(content);
}
<html>
<head>
</head>
<body>
Some more code..........
Get Content
</body>
</html>
I am trying to execute the function from inside the page and trying to get the value. It is giving me undefined error
.getElementsByTagName() returns a NodeList collection of elements. You need to access the first index with [0]. In addition to this, it does not have a .value property. You're looking for .innerHTML instead.
Note that you also shouldn't make use of onclick, and instead should make use of unobtrusive JavaScript by adding an event listener.
This can be seen in the following:
function getContent() {
var content = document.getElementsByTagName('html')[0].innerHTML;
alert(content);
}
document.getElementsByTagName('a')[0].addEventListener('click', getContent);
<html>
<head></head>
<body>
Some more code..........
Get Content
</body>
</html>
Note: this will not work as expected in a Fiddle, but will work as expected on a proper website.
This can be achieved via the innerHTML field of a DOM element. Consider making the following changes to your getContent() function:
function getContent() {
var { innerHTML } = document.querySelector('html');
alert(innerHTML);
}
<html>
<head>
</head>
<body>
Some more code..........
<p> and some more content </p>
Get Content
</body>
</html>

Does jQuery .length defies DOM ready?

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.

innerHTML not displaying

Apologies for asking such a trivial question (just learning how JS works) but I am getting a headache for next to nothing. Maybe I am tired and just don't see what I am doing but why is the below not working - i.e. value of totalBits to print in the body of the 'print' div? If I alert() it shows the value but not using the innerHTML.
<html>
<head>
<title></title>
<script type="text/javascript">
function answer(sentence){
var bitsOfString = sentence.split(" ");
var numOfBits = bitsOfString.length;
return numOfBits;
}
var sentence = prompt("OK, say something!")
var totalBits = answer(sentence);
var div = document.getElementById("print");
div.innerHTML = totalBits;
</script>
</head>
<body>
<div id="print"></div>
</body>
</html>
Because you are calling it before the element is rendered to the page. Move the script after the element is loaded or you can call your code window onload/document ready.
<html>
<head>
<title></title>
</head>
<body>
<div id="print"></div>
<script type="text/javascript">
function answer(sentence) {
var bitsOfString = sentence.split(" ");
var numOfBits = bitsOfString.length;
return numOfBits;
}
var sentence = prompt("OK, say something!")
var totalBits = answer(sentence);
var div = document.getElementById("print");
div.innerHTML = totalBits;
</script>
</body>
</html>
Have a look at your error console. You should get div is null as an error, because the element hasn't been parsed yet.
You need to put your script block after your div element or defer the execution of the script.
Most likely your JavaScritpt executes before the DOM is ready.
In other words, wrap your javascript where you manipulate the div in document.onload function or if you're happy to use jQuery: $(function(){});
You can see it working here: http://jsfiddle.net/c8tyg/
P.S.
I'm not the fan of moving the <script> blocks around because IMHO JavaScript should work regardless how you load it on the page. You're manipulating DOM - respect the game - wait for the DOM to load.

JavaScript question

It is possible not to show html page in user browser until some JavaScript(built-in or in separate file) will be loaded and executed(for page DOM manipulation)?
The easiest thing to do is to set the css variable
display: none;
to the whole page.
then when everything is loaded you can set the display to
display: block; // or something else that suits.
If you make sure that piece of CSS is loaded at the very start of your document it will be active before any html is shown.
if you use a javascript library like jQuery you'll have access to the $(document).ready() function, and can implement a switch over like this:
<html>
<head>
<title>test</title>
<style type="text/css">
body > div {
display: none;
}
</style>
<script type="text/javascript">
$(document).ready(function() {
$('body > div').css('display', 'block');
});
</head>
<body>
<div>
This will initially be hidden.
</div>
</body>
</html>
Not in the classical way you'd distribute a page. Browsers will (usually) start to display chunks of the base HTML file as it arrives.
Of course, you could simulate this by generating all the HTML on the fly from some included Javascript file. But that doesn't sound like a good plan as it will degrade horribly for people without JS enabled, or if you have a minor bug in your script. A better option might be to style the body tag to display: none and restyle it from the script to make certain parts visible again.
What is it you're actually trying to achieve? It sounds like there's likely to be a better way to do this...
Place the content of HTML page in a DIV, make its diplay none and on load of body diplay it.
<script type="text/javascript">
function showContent() {
var divBody=document.getElementById('divBody');
divBody.style.display= 'block';
}
</script>
<body onload="showContent()">
<div id="divBody" style="display: none;">
<--HTML of the page-->
</div>
</body>
Examples of what you might want to do:
Facebook's "BigPipe": http://www.facebook.com/notes/facebook-engineering/bigpipe-pipelining-web-pages-for-high-performance/389414033919
This method allows you to load JS first then ASYNC+inject all DOM content.
GMail
Zimbra (open-source web app similar to MS Outlook/Exchange)
My understanding is that you want to run some javascript code before you load the page. In the js file you write your init function and add the eventlistener to the window on "load" event. This will ensure that the init code gets executed first and then you can start displaying the HTML content.
var Yourdomain = {};
YourDomain.initPage = function(){
/* Your init code goes here*/
}
window.addEventListener("load", YourDomain.initPage, false);
All You really need to do is give your element an ID or CLASS and use the dislay: none; property. When your ready to show it just delete it.
CSS:
#div_1 {
display: none;
}
HTML:
<div id="div_1">
<p>This will be the hidden DIV element until you choose to display it.</p>
<p id="js_1"></p>
<script>
var x = "Some Test ";
var y = "Javascript";
document.getElementById("js_1").innerHTML = x + y;
</script>
</div>

Categories