Johan G
Johan G

Reputation: 1013

Javascript scope issue inside a module

When declared like in the code below, $sessionTimeLeft is undefined when being accessed inside updateSession(). But when I moved $sessionTimeLeft declaration and assignment inside initiate(), I get the correct jQuery object. I think I can access $sessionTimeLeft when it is declared inside initiate() because of closure. But why is $sessionTimeLeft out of scope on my original code?

var session = (function SessionManager() {
  var timeLeftInMs;
  var timeLeftInMin;
  var delay; // in ms
  var $sessionTimeLeft = $('#dcSessionTimeLeft');

  /* Set up the module by setting the timeout and delay time. */
  function initiate(_timeout, _delay) {
    timeLeftInMin = _timeout;
    timeLeftInMs = timeLeftInMin * 60 * 1000;    
    delay = _delay; // delay in ms      

    setInterval(updateSession, delay);
  }

  function updateSession() {
    timeLeftInMs -= delay;
    timeLeftInMin = timeLeftInMs / 60000; // convert ms to min.              

    $sessionTimeLeft.text(timeLeftInMin);
  }

  var publicAPI = {
    initiate: initiate
  };

  return publicAPI;
})();

session.initiate(30,1000);

Upvotes: 1

Views: 56

Answers (2)

Spencer Wieczorek
Spencer Wieczorek

Reputation: 21575

The problem here is that #dcSessionTimeLeft is likely not yet loaded when you try to access it. So one way to ensure it is loaded is to wrap your code in a document.ready:

$(function() {
  var session = (function SessionManager() {
  var timeLeftInMs;
  var timeLeftInMin;
  var delay; // in ms
  var $sessionTimeLeft = $('#dcSessionTimeLeft');

  /* Set up the module by setting the timeout and delay time. */
  function initiate(_timeout, _delay) {
    timeLeftInMin = _timeout;
    timeLeftInMs = timeLeftInMin * 60 * 1000;    
    delay = _delay; // delay in ms      

    setInterval(updateSession, delay);
  }

  function updateSession() {
    timeLeftInMs -= delay;
    timeLeftInMin = timeLeftInMs / 60000; // convert ms to min.              

    $sessionTimeLeft.text(timeLeftInMin);
  }

  var publicAPI = {
    initiate: initiate
  };

  return publicAPI;
  })();

  session.initiate(30,1000);
});

Upvotes: 1

Tejaswi Sharma
Tejaswi Sharma

Reputation: 158

The closure should work absolutely well in this case too. The reason this code is not working because you have not called the session function correctly. Remember this function is returning a object with a function initiate

Last line of the code should be

session.initiate(30,1000);

This should work. To reproduce the issue I used node. I changed the $sessionTimeLeft variable to a string hello and tested if its still accessible in updateSession function. My code is

var session = (function SessionManager() {
  var timeLeftInMs;
  var timeLeftInMin;
  var delay; // in ms
  var $sessionTimeLeft = "Hello";

  /* Set up the module by setting the timeout and delay time. */
  function initiate(_timeout, _delay) {
    timeLeftInMin = _timeout;
    timeLeftInMs = timeLeftInMin * 60 * 1000;
    delay = _delay; // delay in ms

    setInterval(updateSession, delay);
  }

  function updateSession() {
    timeLeftInMs -= delay;
    timeLeftInMin = timeLeftInMs / 60000; // convert ms to min.

    console.log($sessionTimeLeft);
  }

  var publicAPI = {
    initiate: initiate
  };

  return publicAPI;
})();

session.initiate(30,1000);

And the result I got is

Hello
Hello
Hello
Hello
Hello

each every 1 second.

Upvotes: 0

Related Questions