1

My javascript program basically creates a series of images and randomly picks one and sets its src to a particular path. However, for some reason, the code seems to get stuck sometimes. In my program, I'm calling element.parentNode of one of the images in order to surround it with an anchor tag but it throws a console error saying that it is unable to get property 'parentNode' of undefined or null reference despite the fact that sometimes, the code actually executes sometimes without any changes to the code. I have to refresh multiple times before I get lucky enough to have the error not be thrown and the rest of the code executed smoothly. Here is my code:

var id = generateRandom(325);
var el = document.getElementById(id.toString());
var anchor = document.createElement("a");
var nextPage = "level" + (level + 1).toString() + ".html";
if (level == 3) {
    nextPage = "win.html";
}
anchor.setAttribute("href", nextPage);
el.parentNode.insertBefore(anchor, el); // line 54

The error:

    SCRIPT5007: Unable to get property 'parentNode' of undefined or null reference
scripts.js (54,2)

EDIT: updated entire function

var div = document.getElementById("images");
var time = parseInt(document.getElementsByClassName("timer")[0].innerHTML);
var level = getLevel(time);
var rows = 13;
var cols = 23;
for (var i = 1; i <= rows; i++) {
    var marquee = document.createElement("marquee");
    marquee.setAttribute("scrollamount", 30);
            for (var j = 1; j <= cols; j++) {
        var img = document.createElement("img");
        img.setAttribute("src", getPath());
        img.setAttribute("id", (i * j).toString());
        img.setAttribute("width", "80px");
        img.setAttribute("height", "70px");
        marquee.appendChild(img);
    }
    div.appendChild(marquee);
}
var id = generateRandom(rows * cols);
var el = document.getElementById(id.toString());
var anchor = document.createElement("a");
var nextPage = "level" + (level + 1).toString() + ".html";
if (level == 3) {
    nextPage = "win.html";
}
anchor.setAttribute("href", nextPage);
el.parentNode.insertBefore(anchor, el);
var input = document.createElement("input");
input.setAttribute("type", "image");
input.setAttribute("width", "80px");
input.setAttribute("height", "70px");
input.setAttribute("src", "resources\\asotigue.jpg");
el.parentNode.replaceChild(input, el);
anchor.appendChild(input);

generateRandom function:

 function generateRandom(n) {
    return Math.floor((Math.random() * n) + 1);
}
Lexicographical
  • 501
  • 4
  • 16
  • 2
    Obviously your `generateRandom()` function sometimes returns an ID that is non-existent in your DOM. – slash197 Aug 16 '15 at 09:13

2 Answers2

0

Edit now that you've included more code: You are generating 13 * 23 = 299 elements, yet selecting a random element up to 325 so sometimes you don't have enough elements for your random selection.


So, what this code is telling you is that the el variable is undefined or null. There are a couple reasons why that could be:

  1. Your generateRandom(325) is generating an id value that is not present in the page.

  2. You are calling document.getElementById(id.toString()); before the page has finished loading and parsing and thus some page elements are not yet present. Your script should not be run until after the page has been loaded either by moving it to the end of the <body> or by waiting until the DOMContentLoaded event fires.

FYI, if your issue is the second, you can read about various solutions using plain Javascript here pure JavaScript equivalent to jQuery's $.ready() how to call a function when the page/dom is ready for it.


If you know that sometimes generateRandom(325) will pick an element id that is not in the page and you want to allow that, but defend against it stopping execution of your code, then you can just check for that condition:

var id = generateRandom(325);
var el = document.getElementById(id.toString());
var anchor = document.createElement("a");
var nextPage = "level" + (level + 1).toString() + ".html";
if (level == 3) {
    nextPage = "win.html";
}
anchor.setAttribute("href", nextPage);
if (el) {
    el.parentNode.insertBefore(anchor, el);
}

To help you track down your occasional error, right after this:

var id = generateRandom(rows * cols);
var el = document.getElementById(id.toString());

You can add this extra debug code:

// add this to detect what situation you get a missing element
if (!el) {
    console.log("did not find id when: rows = " + rows + ", cols = " + cols");
}
Community
  • 1
  • 1
jfriend00
  • 683,504
  • 96
  • 985
  • 979
  • all the elements I'm looking for are actually generated by the JavaScript code itself so those elements should be present by the time that particular code is run – Lexicographical Aug 16 '15 at 09:27
  • @JavaFanatic - I have no doubt that they "should" be available from your other code, but obviously something isn't right or `el` wouldn't be `null`. Perhaps there is a mistake in your other code that isn't generating everything you thought it would or isn't inserting them all in the DOM so they can be found with `document.getElementById()`. – jfriend00 Aug 16 '15 at 09:30
  • Thanks. :) I realize that my code had an error and that it involved a miscalculation. I changed it so that the upper limit of the random number is actually set by the rows * columns and not a fixed number – Lexicographical Aug 16 '15 at 09:32
  • @JavaFanatic - yeah, I just saw that `13 * 23 = 299` which is smaller than `325` so you're short on elements. – jfriend00 Aug 16 '15 at 09:33
  • I've updated my code once again so that it makes use of 2 variables, rows and columns, for the loops and for the calculation. However, I'm still getting the same error on some occasions. Are there some other errors I may have missed? – Lexicographical Aug 16 '15 at 10:03
  • @JavaFanatic - I've added a debug suggestion to the end of the my answer. – jfriend00 Aug 16 '15 at 15:05
0

This method will collect all the ids on items with a certain class available on the page right now, and will return one of the ids randomly.

You can see it at work in this fiddle. Check the console.

function getIdsListByClassName(className) {
    var elements = document.querySelectorAll('[id].' + className); // get all items that have the id attribute and the defined class name

    var ids = Array.prototype.slice.call(elements, 0).map(function (element) {
        return element.getAttribute('id');
    }); // convert the elements list to array, and map it to ids

    var currentIndex = Math.floor(Math.random() * ids.length); // get an index between 0 and ids.length - 1

    return ids[currentIndex];
}
Ori Drori
  • 183,571
  • 29
  • 224
  • 209