Reputation: 887
Working on a navigation menu script with jQuery. The script is being designed with recursion so that there is no hard coded limit to the number of levels the menu has.
I'll start with the code:
navigationMenu.prototype.reset = function ( ulElement, colorIndex, colors ) { //Color index should always be 1 when directly calling this function
var listItems = $(ulElement.children);
var numItems = listItems.length;
var targetWidth = (100 / numItems) + '%';
listItems.each( function ( x ) {
var children = $(listItems[x].children);
var xT = $(listItems[x]).prop('tagName');
var subMenu = null;
children.each( function ( y ) {
var yT = $(children[y]).prop('tagName');
if (yT == 'UL') {
subMenu = $(children[y]);
} else if (yT == 'A') {
$(children[y]).css('background-color', colors[colorIndex-1]); //Offset by 1 to facilitate for 0 indexed arrays
$(children[y]).hover( function () { //Set hover color to the opposite
$(children[y]).css('background-color',colors[(3-colorIndex)-1]); //3-1 = 2 and 3-2 = 1, subtract 1 to facilitate 0 indexed arrays
}, function() {
$(children[y]).css('background-color',colors[colorIndex-1]); //3-1 = 2 and 3-2 = 1, subtract 1 to facilitate 0 indexed arrays
}); //Rest of style SHOULD be handled by css (width 100%, text color, text align)
}
});
if (subMenu !== null) { //Recurse
navigationMenu.prototype.reset(subMenu, (3 - colorIndex), colors); //Not defined?
}
if (xT == 'LI') { //Format the element
$(listItems[x]).css('width',targetWidth);
$(listItems[x]).css('background-color', colors[colorIndex]);
}
});
};
Next, The error:
Uncaught TypeError: Cannot read property 'firstChild' of null <-whitespace-> jquery-1.11.1.min.js:2
What concerns me is that the error does not seem to come directly from my code, rather, a function within the jQuery library; however, I'm placing good money on the fact that it is because of something I did wrong.
A live demo can be found here: http://proofoftheilluminati.com/test/test.html
For an idea of the final look of the menu you can see the top level with hover effect and a simple JS script that maths the link widths here: http://proofoftheilluminati.com/test/index.html
Script: http://proofoftheilluminati.com/test/scripts/menu.js
I'm hosting a freshly downloaded copy of jQuery version 1.11.1: http://proofoftheilluminati.com/test/scripts/jquery-1.11.1.min.js
What it should be doing: Top level list should be orange with black over effect second level list should be black with orange hover effect third level list should be same as first, etc.
Positioning is handled by external css file
What it is doing: Handles top level list correctly, seems to error before style second level list.
Please let me know if I left anything out. I try to be thorough.
Edit: The supplied code has a comment on the line that calls itself:
//Not defined?
This was left over from a previous error, I was having trouble getting it to recognize the recursive function call. I tried the following lines here and they would not allow the function to progress:
this.reset(subMenu, (3 - colorIndex), colors);
reset(subMenu, (3 - colorIndex), colors);
navigationMenu.reset(subMenu, (3 - colorIndex), colors);
Additionally, this function is called when the document is ready:
$(document).ready(function() {
s = new navigationMenu('#NavMenu', '#884106', '#000000', -1);
});
Edit: modified code to use x/y instead of index and xT/yT instead of tag (removed nested variables with same name)
Upvotes: 0
Views: 119
Reputation: 21482
When you first call navigationMenu.prototype.reset
, I'm guessing ulElement
is a DOM element, but when you call it recursively, you are passing it subMenu
, which is a jQuery object. That will be a problem for the following line:
var listItems = $(ulElement.children);
Try changing the following line of code:
navigationMenu.prototype.reset(subMenu, (3 - colorIndex), colors);
To:
navigationMenu.prototype.reset(subMenu[0], (3 - colorIndex), colors);
I prefer to always prefix variables that refer to jQuery objects with "$" to keep them straight.
You could also use this
inside the functions given to .each()
. So instead of:
children.each(function(index) {
var tag = $(children[index]).prop('tagName');
You could have:
children.each(function() {
var $child = $(this),
tag = $child.prop('tagName');
You could also consider using the jQuery .children()
method, instead of the children
DOM element property
Upvotes: 1