2

So I have this very weird issue where calling console.log in JavaScript is actually printing out the contents of a tag, whose id attribute is identical to the name of the variable I've passed in.

This is my code:

<div id="newTypes">
    <div id="noNewTypes" style="text-align: center; width: 100%; margin: 25% 0;">
        ...No new device types to show...
    </div>
    <div id="XNewTypes">
        <script>
            //newTypes has the same name as the parent container's id
            console.log(newTypes);
        </script>
    </div>
    <script>
        $("#btnNewTypes").on("click", function() {          
            if (newTypes.length == 0) {
                $("#noNewTypes").show();
                $("#XNewTypes").hide();
            }
            else {
                $("#noNewTypes").hide();
                $("#XNewTypes").show();
            }
        });
    </script>
</div>

newTypes is actually defined farther up in the code, it's an array containing just a bunch of strings. But I really don't think that's relevant to this issue.

Now, here's the output to the console of console.log(newTypes); enter image description here (Sorry for the small image)

So I'm super confused about why this would be printing out the tag's contents instead of the variable name, because I am NOT calling console.log($("#newTypes").text());, I am passing the variable as parameter.

And then what's possibly even more bizarre, since this is causing an error for some reason, I decided first to take the easy way out and just rename the variable to newTypeArr, but then when I call console.log(newTypeArr);, what I get is this: enter image description here

instead of the expected undefined.

I don't think this is relevant, I'm pretty sure the above HTML is all that's needed, but here's the whole part of the DOM that comes into play here that might be relevant. And as you can see, this whole thing is inside a PHP web page, again though, shouldn't be relevant.

<div id="tblLatest">
    <?php searchFilterForm("tblLatest"); ?> 
    <script>
        var dataLatest = <?php echo getLatestDeviceTypes(); ?>;
        for (val of dataLatest) {
            dataLatest[dataLatest.indexOf(val)] = val.replace("\n", "");
        }
        buildTable(dataLatest, "tblLatest");

        setTimeout(function() {
            newTypeArr = ((all, late) => {
                var ret = []
                for (val of late) 
                    if (all.indexOf(val) == -1)
                        ret.push(val);
                return ret;
            })(dataAll, dataLatest); 

        }, 1000);
    </script>
</div>
<div id="newTypes">
    <div id="noNewTypes" style="text-align: center; width: 100%; margin: 25% 0;">
        ...No new device types to show...
    </div>
    <div id="XNewTypes">
        <script>
            //newTypes has the same name as the parent container's id
            console.log(newTypes);
        </script>
    </div>
    <script>
        $("#btnNewTypes").on("click", function() {          
            if (newTypes.length == 0) {
                $("#noNewTypes").show();
                $("#XNewTypes").hide();
            }
            else {
                $("#noNewTypes").hide();
                $("#XNewTypes").show();
            }
        });
    </script>
</div>

Now, newTypes / newTypeArr, whichever it's named, is declared inside a setTimeout function, which is asynchronous, therefore it's likely that newTypes / newTypeArr will not be defined and would print undefined as I mentioned, however that's something I can work around. It should NOT be printing either the tag whose id matches newTypes or an error though, which is what's confusing me.

nixin72
  • 322
  • 4
  • 16

2 Answers2

2

It is a little known fact, that providing an ID on an element tag, will create a corresponding GLOBAL javascript variable of the same name.

Kyle Simpson: Global DOM variables

Basically, you need to rethink, that you are essentially declaring the varibale twice, by naming the variable the same as your ID. Whats more, one of those declarations lies in the global scope, so will never be undefined.

The solution is simple. Don't use ID's defined in HTML as javascript variable names OR don't use id's on your HTML elements.

Does make you wonder why we need document.getElementById though right?

samazi
  • 1,161
  • 12
  • 24
  • After testing it out quickly, I'm unsure of an effective use of `document.getElementById` either. My initial thought was that calling the function would throw an error if the id didn't exist whereas using the global variable would just be null or undefined, but both methods of accessing it return null, which odd to me. using `document.getElementById` must be safer in some way. – nixin72 Jun 09 '17 at 16:21
  • 1
    It's definitely a quirk. I also tried this out a while ago, and couldn't really see the reasoning. Yes, always safer to use the current standard method of access to `DOM` elements. When the `legacy browser` factor is gone, they will drop this behavior, so if your code relies on global variables for DOM elements, your page will instantly break when the change happens. – samazi Jun 09 '17 at 17:01
  • Yea, that's true. Web standards exist for a reason. I'm going to continue using `document.getElementById`, or since I'm using jQuery just `$("#id")`, which is so much easier... – nixin72 Jun 09 '17 at 17:15
2

Getting an element using window[element id] or window[element name] is standard behavior implemented by all modern browsers since Firefox 14.

This answer explains why providing an element ID will return the contents of that element.

Regarding your variable problem, this is a scope issue. Defining your variable globally will fix the problem.

var newTypeArr;
...
    setTimeout(function() {
        newTypeArr = ((all, late) => {
            var ret = []
            for (val of late) 
                if (all.indexOf(val) == -1)
                    ret.push(val);
            return ret;
        })(dataAll, dataLatest); 

    }, 1000);
...
console.log(newTypeArr);
erichunt
  • 123
  • 1
  • 3
  • 10
  • shouldn't declaring a variable without the keyword `var` or `let` automatically set it's scope to be global though? And why does it show up with an error as opposed to `undefined`? – nixin72 Jun 09 '17 at 16:13
  • 1
    Oh wait, never mind. I just found the answer to my own question. It's scope is global, however the asynchronous function didn't finish executing by the time I tried to access the variable. However, because the variable name had never even been declared, never mind the variable getting instantiated, that will throw an error because it has no idea what I'm referencing. If I were to use `var newTypeArr;` and never instantiate it, it will be undefined. But as is, throwing an error makes sense because it's not just undefined, it's literally nothing, it has no idea what I'm doing. – nixin72 Jun 09 '17 at 16:25