-1

The following is my HTML page:

<html>

<head>
    <script type="text/javascript" src="/game/js/ready/main.js" ></script>
    <link rel="stylesheet" type="text/css" href="/game/css/nav.css" />
</head>

<body>

    <div class="nav-buttons">
        <span class="nav-button" onclick="f()">Show A</span>
        <span class="nav-button" onclick="h()">Show B</span>
        <span class="nav-button" onclick="g()">Show C</span>
    </div>

    <div id="divA">
    a
    </div>

    <div id="divB">
    b
    </div>

    <div id="divC">
    c
    </div>

</body>

</html>

And the following is my main.js file:

var divA = document.getElementById("divA");
var divB = document.getElementById("divB");
var divC = document.getElementById("divC");

function f(){
    window.divA.style.display = "block";
    window.divB.style.display = "none";
    window.divC.style.display = "none";
}

function h(){
    window.divA.style.display = "none";
    window.divB.style.display = "block";
    window.divC.style.display = "none";
}

function g(){
    window.divA.style.display = "none";
    window.divB.style.display = "none";
    window.divC.style.display = "block";
}

As far as I could tell, this is valid. However, when I call f(), I get an error saying that all of the above defined variables are null.

When changing the var keyword to let, the above code runs with no problems.

Which is really weird, because as far as I understood from this answer, the opposite should be the case.

Is this a misunderstand of mine, or a weird bug in my browser ?

Community
  • 1
  • 1
Mickey695
  • 98
  • 8
  • You want to try something simpler than DOM manipulation to test this since the state of the DOM might not be what you think it is when you call f. In fact, you should post your HTML snippet with the divs and the js code so that the interaction can be seen and tried by others – pvg Dec 17 '16 at 11:59
  • I edited the questions so that it would be more clear. Obviously I meant that I called the f() function and not referenced a variable f. – Mickey695 Dec 17 '16 at 12:00
  • 1
    it's still incomplete. Make a tiny html page that demonstrates the issue as you describe it. – pvg Dec 17 '16 at 12:01
  • [How to create a Minimal, Complete, and Verifiable example](https://stackoverflow.com/help/mcve) – Andreas Dec 17 '16 at 12:01
  • 2
    I bet you everything it's because when `document.getElementById` runs, the elements are not there. Try changing the names of the variables to something else and it would work consistently. Reason is that if you do `window.divA` that will _give you_ an element with an ID of `divA` if one such exists. What happens is that you look them up, they aren't there, they appear but you've already overriden `window.divA` and when you call your function you get `null`. With `let` you will get consistent behaviours, since `window.divA` will actually _give you_ the div – VLAZ Dec 17 '16 at 12:04
  • @pvg I added my document as an example. – Mickey695 Dec 17 '16 at 12:11

3 Answers3

5

Your var variables are declared in global scope and can be accessed as global properties. Their value is null because of Why does jQuery or a DOM method such as getElementById not find the element?

With let it seems to work because let variables are not available as properties of the global object. So what you get is not your variable, but the global polluter. The global polluter is a dirty dark magic thingie which makes elements available on the global object by their ID. It should have never existed. Do not rely on it. It's bad practice.

console.log(window.divA); // Horrendous !!!!
<div id="divA"></div>
Community
  • 1
  • 1
Oriol
  • 225,583
  • 46
  • 371
  • 457
  • 5
    `It should have never existed.` most definitely. _Thanks, Microsoft!_ – VLAZ Dec 17 '16 at 12:06
  • It was created in IE (a long time ago) to cut some corners. As with all *cutting-corner-methods* you have to be aware on what you are doing. **But**, all other Browser vendors accepted it to become a W3C standard. So you can't just blame Microsoft. Since it is a W3C standard you can rely on it. Yes, it might be dark magic (because you don't read W3C standards) ... but the magic does work... Try it with duplicated IDs – Danny '365CSI' Engelman Nov 28 '18 at 12:47
2

Notice that you're not getting an error that variables are not declared but that the values they are holding are null at the time you're trying to access them (when you call f()).

This means that at the time of declaration, or in other words at the time you assign them values from document.getElementById, there are no elements with ids divA, divB and divC.

var divA = document.getElementById("divA"); // null
var divB = document.getElementById("divB"); // null
var divC = document.getElementById("divC"); // null

function f(){
    window.divA.style.display = "block"; // error because window.divA is null
    window.divB.style.display = "none";  // error because window.divB is null
    window.divC.style.display = "none";  // error because window.divC is null
}

f();
<!-- DOM IS EMPTY -->

The difference when using let is that the variables won't be assigned to the window object. Their values will still be null.

let divA = document.getElementById("divA"); // null
let divB = document.getElementById("divB"); // null
let divC = document.getElementById("divC"); // null

function f(){
    window.divA.style.display = "block"; // error because window.divA is undefined
    window.divB.style.display = "none";  // error because window.divB is undefined
    window.divC.style.display = "none";  // error because window.divC is undefined
}

f();
<!-- DOM IS EMPTY -->

See Oriol's answer for explanation on why let seems to work

Community
  • 1
  • 1
nem035
  • 31,501
  • 5
  • 73
  • 88
-1

Try this, you need to call the javascript after the page loading completes, for that you need to use onload method. Before loading if you call the javascript divA,divB,divC are gives null value. To avoid this null values you need to use this within your f() function and call the function after page gets loaded by using onload method

<script>
  function f() {
    alert("function works");
    var divA = document.getElementById("divA");
    var divB = document.getElementById("divB");
    var divC = document.getElementById("divC");

    divA.style.display = "block";
    divB.style.display = "none";
    divC.style.display = "none";
  }
</script>

<body onload="f();">
  <div id="divA" style="width:100px;height:100px;background-color:Red;"></div>
  <div id="divB" style="width:100px;height:100px;background-color:green;"></div>
  <div id="divC" style="width:100px;height:100px;background-color:blue;"></div>
</body>
vijay
  • 921
  • 6
  • 15
  • There are much better ways to execute code after the rest of page loads, for example an external ` – Michał Perłakowski Dec 17 '16 at 12:41
  • I agree your point but this also one of the method used in many scenario. This is useful for the beginner to learn each method easily. for example beginner have to know when to use onload,onclick,onchange method. – vijay Dec 17 '16 at 14:37