4

I cannot access button or container variable inside the functions. What's the problem?

var Site = {
    init: function () {
        $(".site .head .nav-bar .nav-button").click(function () {
            Site.Navigation.toggle();
            return false;
        });
    },
    Navigation: {
        container: $(".site .head .nav-bar .navigation"),
        button: $(".site .head .nav-bar .nav-button"),
        toggle: function () {
            if (this.button.hasClass("active")) {
                this.hide();
            }
            else {
                this.show();
            }
        },
        show: function () {
            this.button.addClass("active");
            this.container.slideDown();
            return false;
        },
        hide: function () {
            this.button.removeClass("active");
            this.container.slideUp();
            return false;
        }
        //another stuff
    }

}

$(document).ready(function () {
    Site.init();
});
5
  • 5
    How are you calling toggle? Commented Aug 9, 2013 at 21:54
  • Is it possible that your jQuery selectors are not returning any elements? Can we see some html? Commented Aug 9, 2013 at 22:02
  • 1
    It works here: jsfiddle.net/ec2Y6/1 Commented Aug 9, 2013 at 22:02
  • @Austen when I call function directly from jquery object, It works: e.g. $(".site .head .nav-bar .nav-button").addClass("active"). Commented Aug 9, 2013 at 22:06
  • Check out kalley's fiddle. If you can't get it working using that, then bfavaretto is probably right and there is an issue with your script placement. Commented Aug 9, 2013 at 22:11

2 Answers 2

4

I'm not absolutely sure, but I believe what's happening is that your selectors are not matching any elements, because they're called before the elements are added to the DOM. I mean these:

container: $(".site .head .nav-bar .navigation"),
button: $(".site .head .nav-bar .nav-button"),

Try moving your whole JavaScript to the end of the body (right before </body>).

Another possible solution is to only populate those properties from Site.init (currently, they're populated immediately when the object literal is declared:

var Site = {
    init: function () {

        this.Navigation.container = $(".site .head .nav-bar .navigation");
        this.Navigation.button = $(".site .head .nav-bar .nav-button");

        $(".site .head .nav-bar .nav-button").click(function () {
            Site.Navigation.toggle();
            return false;
        });
    },
    Navigation: {
        container: null,
        button: null,
        toggle: function () {
            if (this.button.hasClass("active")) {
                this.hide();
            }
            else {
                this.show();
            }
        },
        show: function () {
            this.button.addClass("active");
            this.container.slideDown();
            return false;
        },
        hide: function () {
            this.button.removeClass("active");
            this.container.slideUp();
            return false;
        }
        //another stuff
    }

}
Sign up to request clarification or add additional context in comments.

3 Comments

exactly. Why this happen? I run my init function after ready $(document).ready(function () { Site.init();
That code runs before Site.init. The Navigation object is created immediately when the object literal is declared, and the values of container and button are already defined (as empty jQuery objects) when init runs. You can move that logic to init, e.g., this.Navigation.container = ...)
@Sahar, Don't do this, it's a very bad practice to put this kind of module definition code inside $(document).ready. Use the approach I suggested instead. What's happening is that the values are assigned to container and button way before Site.init is called, since the assignments are happening as soon as the object gets created.
2

It's because your elements probably don't exist yet when your object is defined, since the code isin't running after the DOM is ready.

You could modify your Site.init function to accept these element references as argument however.

   init: function (container, button) {
        var nav = this.Navigation;

        nav.container = container;
        nav.button = button;
    }

Then pass in the elements:

$(document).ready(function () {
    Site.init(/*containerEl*/, /*buttonEl*/);
});

1 Comment

Upvoted. I added another init function for Navigation to do something like that.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.