0

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>
2
  • It is very hard to understand looking at so many lines of code. You should try to put only the code where mostly likely the problem is. Commented Jan 24, 2012 at 15:37
  • I think the problem lies in the structure, so I thought it would be best to show everything. I'll try to make it shorter Commented Jan 24, 2012 at 15:41

4 Answers 4

1

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?

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

5 Comments

Yes exactly! I have noticed this to, but what is the best solution for making sure that each function uses the same variables, while not creating their own variables, which creates unwanted behavior. Could reinitializing in the ajaxComplete function be a solution?
All your variables (with exception to the window.Slider bit) appear to be scoped to the function(window,undefined){...} method. Unless you accidentally introduce a var in front of them in one of your functions you should be OK.
Secondly, you probably don't need the window.Slider reference unless you need reference to that elsewhere. You can use functions in setInterval so window.setInterval(function(){Slider.move("right")},intervalSpeed); should work, and Slider would be the one in local scope, not window.Slider.
In a situation where the slider isn't loaded through AJAX this is the case, but on a AJAX load the variables and their DOM values I declare at the beginning of the function are empty.
JayC, your right about the window reference, which caused a problem with interval multiplication. A deleted it and it works as supposed
0

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

1 Comment

We're getting off topic here but that line of code gets executed only once. So would it really matter?
0

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');
});

1 Comment

I think this is not the problem, see answer of JayC
0

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.

4 Comments

Indeed Jochem. I have not been clear on this, but this is the case. I'm now searching for the best solution of reinitializing the variables so that every function can make use of the same variable after the AJAX load of the slider
Perhaps you should not redeclare your variables. Why not simply use $('#slider_control') within your callback routines?
This could be possible, but won't there be discrepancies between the different functions?
The function $('#slider_control') looks at that moment in time in the current DOM for an element with the given ID. Next time the (callback) function runs, it may point to a newly created element.

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.