Ruben
Ruben

Reputation: 89

Javascript different variable scope on AJAX call

I have a problem with my variable scope in a simple slider script that I´ve written (I don't want to use a readymade solution because of low-bandwidth). The slider script is called on statically loaded pages (http) as well as on content loaded through AJAX. On the statically loaded page (so no AJAX) the script seems to work perfect. However when called through AJAX the methods called can't find the elements of the DOM, which halts the necessay animation that is needed for the slider.

All the events are handled through even delegation (using jQuery's on() function), this however provided no solution. I'm quite sure it has something to do with the structure and variable scope of the script, but am unable to determine how to change the structure. So I'm looking for a solution that works in both situations (called normal or through AJAX).

I tried to declare the needed variables in every function, this however resulted in some akward bugs, like the multiplication of the intervals I set for the animation, because of the function scope. Hope somebody can help me in the right direction.

// Slider function
(function (window, undefined) {
    var console = window.console || undefined, // Prevent a JSLint complaint 
        doc = window.document,
        Slider = window.Slider = window.Slider || {},
        $doc = $(doc),
        sliderContainer = doc.getElementById('slider_container'),
        $sliderContainer = $(sliderContainer),
        $sliderContainerWidth = $sliderContainer.width(),
        slider = doc.getElementById('slider'),
        $slider = $(slider),
        $sliderChildren = $slider.children(),
        $slideCount = $sliderChildren.size(),
        $sliderWidth = $sliderContainerWidth * $slideCount;
    $sliderControl = $(doc.getElementById('slider_control')),
    $prevButton = $(doc.getElementById('prev')),
    $nextButton = $(doc.getElementById('next')),
    speed = 2000,
    interval,
    intervalSpeed = 5000,
    throttle = true,
    throttleSpeed = 2000;

    if (sliderContainer == null) return; // If slider is not found on page return           

    // Set widths according to the container and amount of children 
    Slider.setSliderWidth = function () {
        $slider.width($sliderWidth);
        $sliderChildren.width($sliderContainerWidth);
    };

    // Does the animation
    Slider.move = function (dir) {
        // Makes use of variables such as $sliderContainer, $sliderContainer width, etc.
    };

    // On ajax call
    $doc.on('ajaxComplete', document, function () {
        Slider.setSliderWidth();
    });

    // On doc ready
    $(document).ready(function () {
        Slider.setSliderWidth();
        interval = window.setInterval('Slider.move("right")', intervalSpeed);
    });

    // Handler for previous button
    $doc.on('click', '#prev', function (e) {
        e.preventDefault();
        Slider.move('left');
    });

    // Handler for next button
    $doc.on('click', '#next', function (e) {
        e.preventDefault();
        Slider.move('right');
    });

    // Handler for clearing the interval on hover and showing next and pervious button  
    $doc.on('hover', '#slider_container', function (e) {
        if (e.type === 'mouseenter') {
            window.clearInterval(interval);
            $sliderControl.children().fadeIn(400);
        }
    });

    // Handler for resuming the interval and fading out the controls
    $doc.on('hover', '#slider_control', function (e) {
        if (e.type !== 'mouseenter') {
            interval = window.setInterval('Slider.move("right")', intervalSpeed);
            $sliderControl.children().fadeOut(400);
        }
    });
})(window);

The HTML example structure:

<div id="slider_control">
   <a id="next" href="#next"></a>
   <a id="prev" href="#prev"></a>
</div>
<div id="slider_container">
   <ul id="slider">
      <li style="background-color:#f00;">1</li>
      <li style="background-color:#282">2</li>
      <li style="background-color:#ff0">3</li>
   </ul>
</div>

Upvotes: 0

Views: 586

Answers (4)

JayC
JayC

Reputation: 7141

I notice you have

Slider.setSliderWidth = function() {
    $slider.width($sliderWidth);
    $sliderChildren.width($sliderContainerWidth);
};

which is called on ajax complete.

Does you ajax update the DOM giving a new DOM element that you could get to by doc.getElementById('slider')? Then your var slider and jquery var $slider are likely pointing to things that no longer exist (even if there is a dom element with slider as the id). To rectify, whenever the ajax is invoked that replaces that element, reinitialize slider and $slider to point to the new jquery wrapped element using the same initialization you have.

slider = doc.getElementById('slider');
$slider = $(slider);

Edit:

I'm not sure where you're going with the variable scope issue, but take a look at this example.

<pre>
<script> 
(function(){
   var a  = "something";
   function x (){
      a += "else";
   }
   function y() {
      a = "donut";
   }
   function print (){
      document.write(a +"\n");
   }
   print ();
   x();
   print ();
   y();
   print ();
   x();
   print ();
})();
document.write(typeof(a) + "\n");
</script>
</pre>

It outputs into the pre tag

something
somethingelse
donut
donutelse
undefined

This isn't all that different from what you're already doing. As long as a is not a parameter of a method and is not declared with var in a nested scope, all references to a in code defined within your function(window,undefined){ ...} method will refer to that a, given that a is defined locally by var to that method. Make sense?

Upvotes: 1

Jochem
Jochem

Reputation: 2994

Wait, what gets loaded through Ajax? The slider-html code? In that case, the Slider has already been 'created' and a lot of your variables will point to nowhere (because these DOM elements did not existed when the variables were initialized). And they will never do so either.

Upvotes: 0

Jivings
Jivings

Reputation: 23250

I think that when you use on it doesn't search the element for the selector as you are assuming. So you would have to use:

$doc.on('click', '#slider_control #prev',function(e){
    e.preventDefault();
    Slider.move('left');
});

Upvotes: 0

satoshi
satoshi

Reputation: 4103

To begin, surely you can replace all the getElementById using a jQuery approach. i.e. replace $(doc.getElementById('next')) with $('#next')

Upvotes: 0

Related Questions