javascriptcssdomself-executing-function

Eliminate undefined TypeError in self-executing anonymous function


I have a script that gives me the following error: 'TypeError: clickables[ic] is undefined' when I'm checking it with Firebug/in browser consoles. I'm a javascript beginner, who is trying to learn how to do things in vanilla javascript, and so I'm looking specifically for a solution that is just that.

The question: How do I get rid of/silence the undefined TypeError?

What the script should be doing:

I'm using this to reveal hidden elements, whose display attribute is set to none. The script should be getting all the instances of a particular class in a document, .item-reveal, joining that with a unique ID that each item having that class is given, to form a new class to search for via getElementsByClassName. The items with the .item-reveal class are items that are clicked on, the item that is unhidden/revealed has the .ID-reveal-item class (the unique ID of the clickable element followed by the .item-reveal class name reversed, for a simple convention). The ID isn't used for stying at all, it's merely to create a unique class based on a naming convention that can be applied to any pair of elements: one that is clicked on, one that is unhidden/hidden via creating/changing a style for the display attribute.

What the script does:

Currently, the script actually reveals the items onclick, and hides them again on subsequent clicks, and it works with multiple items. So, it kind of, basically, works. I just can't figure out the 'TypeError: clickables[ic] is undefined' issue and how to get rid of it. I get it in several browsers when using developer tools.

The script is an attempt at a self-executing anonymous function sort of thing, so I know the convention is a bit different, but I'm wanting to stick with it so I can apply it to other uses down the road. The article that inspired it is found here:

http://esbueno.noahstokes.com/post/77292606977/self-executing-anonymous-functions-or-how-to-write

EXAMPLE:

HTML

<!-- Item to be clicked, with unique ID -->
<h3 class="item-reveal" id="plan-1">Click for special pricing!</h3>

<p>An introductory paragraph...</p>

<!-- Hidden item to be revealed, will always have a unique class -->
<p class="plan-1-reveal-item">Special, this month only: $49.99</p>

<h3 class="item-reveal" id="plan-b">Click for special pricing!</h3>
<p>An introductory paragraph...</p>
<p class="plan-b-reveal-item">Special, this month only: $29.99</p>

CSS

/* Init - hide/unhide onclicks */
.item-reveal                {cursor:pointer;}
[class$="-reveal-item"]     {display:none;}
/* Halt - hide/unhide onclicks */

javascript:

var clickables = document.querySelectorAll('.item-reveal');
var clickCount = clickables.length;
(function () {
    var Reveal = {
        swapThis: function () {
            for (var ic = 0; ic <= clickCount; ic += 1) {
                // Next line seems to create the error message.
                clickables[ic].onclick = function (unhideHide) {
                    var hidden = this.id;
                    var findClass = hidden += '-reveal-item';
                    var revealSwap = document.getElementsByClassName(findClass);
                    for (rn = 0; rn < revealSwap.length; rn++) {
                        revealSwap[rn].style.display = (revealSwap[rn].style.display == 'block') ? 'none' : 'block';
                    }
                }
            }
        }
    }
    Reveal.swapThis();
}) ();

The script is linked via a SCRIPT tag, just prior to the closing BODY tag. I have tried it with both Async and Defer attributes, with and without other scripts in an HTML document, and the result is the same. I tried adding an event handler to ensure it wasn't something with the DOM loading still ongoing, but I'm not sure how to really test for that to see if it was actually doing anything. Unit testing is something that I'm just starting to attempt familiarizing myself with.

I'm trying to knock the dust off skills after several years in a completely unrelated industry, so the last year has been all about catching up on web development technologies, learning responsive design and HTML5 data stuff, and trying to learn javascript. I've searched, read, and bought several ebooks/books, and this is one of the few times I've run into something I just can't figure out. I imagine it's probably something simple and obvious to someone with formal programming/scripting knowledge, but I was an eBusiness major and networking, marketing, server/systems support, cabling, HTML/CSS, etc., are where I'm comfortable. Any help is greatly appreciated, but keep in mind that I'm trying to implement this in an environment/project that will have no jQuery, by choice. Thanks!


Solution

  • You are going off the end of the list with this:

    for (var ic = 0; ic <= clickCount; ic += 1) 
    

    Change it to this:

    for (var ic = 0; ic < clickCount; ic += 1) 
    

    clickCount is the length of the list so since it's 0 based indexing, clickables[clickCount - 1] is the last element in the list. You were trying to access clickables[clickCount] which does not exist.