最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - How do I run a custom function ONLY the first time a user scrolls to a certain element? - Stack Overflow

programmeradmin4浏览0评论

The problem:

I have a fancy looping image carousel that needs to start—from a specific first slide—when the user scrolls to a certain div. It should NEVER restart if the user scrolls up/down and back to that div.

I can only currently get it to start when the user scrolls to the div—and then it gets screwed up when you scroll away and back to it, which I assume is because the function starts running again.

What I'm trying to achieve:

  1. User scrolls to certain div
  2. Fancy image carousel animation function runs
  3. If user scrolls up/down and back to the div, the animation function never starts again.

My (anonymized, sorry guys!) code:

/

HTML

<p class="scroll_down">Scroll down...</p>

<div class="animation_container">You should get an alert only once here—the first time you scroll to this div.</div>

CSS

.scroll_down {
  margin-bottom: 1000px;
}

.animation_container {
  width: 300px;
  height: 200px;
  background-color: red;
  padding: 30px;
  color: white;
  margin-bottom: 1000px;
}

jQuery

// The fancy function for my animations
function doSomeComplicatedStuff() {
  alert("...and here's where the plicated animations happen!");
}

// The function to check if div.animation_container is scrolled into view
function isScrolledIntoView(elem)
{
  var docViewTop = $(window).scrollTop();
  var docViewBottom = docViewTop + $(window).height();

  var elemTop = $(elem).offset().top;
  var elemBottom = elemTop + $(elem).height();

return ((elemTop <= docViewBottom) && (elemTop >= docViewTop));
}

// If div.animation_container is scrolled into view, run the fancy function
$(window).on('scroll', function() {
  if (isScrolledIntoView('.animation_container')) {
    run_once(function() {
      doSomeComplicatedStuff();
    });
  }
});

// The function that's supposed to make sure my fancy function will only run ONCE, EVER
function run_once( callback ) {
  var done = false;
  return function() {
    if ( !done ) {
      done = true;
      return callback.apply( this, arguments );
    }
  };
} 

Apologies for the script clearly written by a visual designer. Let me know if the question isn't clear enough from the anonymized code.

The problem:

I have a fancy looping image carousel that needs to start—from a specific first slide—when the user scrolls to a certain div. It should NEVER restart if the user scrolls up/down and back to that div.

I can only currently get it to start when the user scrolls to the div—and then it gets screwed up when you scroll away and back to it, which I assume is because the function starts running again.

What I'm trying to achieve:

  1. User scrolls to certain div
  2. Fancy image carousel animation function runs
  3. If user scrolls up/down and back to the div, the animation function never starts again.

My (anonymized, sorry guys!) code:

http://jsfiddle/annablabber/h8pqW/

HTML

<p class="scroll_down">Scroll down...</p>

<div class="animation_container">You should get an alert only once here—the first time you scroll to this div.</div>

CSS

.scroll_down {
  margin-bottom: 1000px;
}

.animation_container {
  width: 300px;
  height: 200px;
  background-color: red;
  padding: 30px;
  color: white;
  margin-bottom: 1000px;
}

jQuery

// The fancy function for my animations
function doSomeComplicatedStuff() {
  alert("...and here's where the plicated animations happen!");
}

// The function to check if div.animation_container is scrolled into view
function isScrolledIntoView(elem)
{
  var docViewTop = $(window).scrollTop();
  var docViewBottom = docViewTop + $(window).height();

  var elemTop = $(elem).offset().top;
  var elemBottom = elemTop + $(elem).height();

return ((elemTop <= docViewBottom) && (elemTop >= docViewTop));
}

// If div.animation_container is scrolled into view, run the fancy function
$(window).on('scroll', function() {
  if (isScrolledIntoView('.animation_container')) {
    run_once(function() {
      doSomeComplicatedStuff();
    });
  }
});

// The function that's supposed to make sure my fancy function will only run ONCE, EVER
function run_once( callback ) {
  var done = false;
  return function() {
    if ( !done ) {
      done = true;
      return callback.apply( this, arguments );
    }
  };
} 

Apologies for the script clearly written by a visual designer. Let me know if the question isn't clear enough from the anonymized code.

Share Improve this question asked Jul 16, 2014 at 21:45 AnnaBlabberAnnaBlabber 4421 gold badge7 silver badges21 bronze badges 2
  • Your fiddle seems to not be working – Justin C Commented Jul 16, 2014 at 21:54
  • Sorry, I was trying so many different things to achieve what I wanted that I left some broken code in there. I guess it didn't match up to what I described. – AnnaBlabber Commented Jul 16, 2014 at 22:10
Add a ment  | 

4 Answers 4

Reset to default 5

Move that done variable into a global?

var firstScroll = false;

$(window).on('scroll', function() {
  if (isScrolledIntoView('.animation_container') && !firstScroll) {
      doSomeComplicatedStuff();
  }
});

function doSomeComplicatedStuff() {

    firstScroll = true;        

    // Your code here
}

This way, the first time isScrolledIntoView returns true, the doComplicatedStuff function immediately flips the boolean firstScroll that stops subsequent calls.

You can have something like this

// The fancy function for my animations
var alreadyRun = false;
function doSomeComplicatedStuff() {
    alert("...and here's where the plicated animations happen!");
}

// The function to check if div.animation_container is scrolled into view
function isScrolledIntoView(elem)
{
    var docViewTop = $(window).scrollTop();
    var docViewBottom = docViewTop + $(window).height();

    var elemTop = $(elem).offset().top;
    var elemBottom = elemTop + $(elem).height();

    return ((elemTop <= docViewBottom) && (elemTop >= docViewTop));
}

// If div.animation_container is scrolled into view, run the fancy function
$(window).on('scroll', function() {
  if (isScrolledIntoView('.animation_container') && !alreadyRun) {
      doSomeComplicatedStuff();
      alreadyRun = true;
  }
});

Working jsfiddle

  1. Put that done variable into the global scope. Else it is redefined again and again every time that run_once() is called. A better approach would be adding a first parameter to that function to identify which action is ran or not (function run_once(identifier,callback) { if(!done[identifier]) { run function and set done[identifier] to true; })
  2. Remove that return function() from run_once()
  3. See Fiddle

Here is an updated solution: http://jsfiddle/h8pqW/2/

Basically, you were running run_once every time the condition was met, so it was creating a new done variable. I moved the run_once call out into the doSomeComplicatedStuff declaration, converting that to a method that will only run once.

// The fancy function for my animations
var doSomeComplicatedStuff = run_once(function () {
    alert("...and here's where the plicated animations happen!");
});

Here is a more performant alternative that does not require the use of a run_once helper: http://jsfiddle/h8pqW/3/

In this example, I simply unbind the scroll event as soon as the function is called. This will prevent the page from continuing to try to run the function each time the user scrolls.

// If div.animation_container is scrolled into view, run the fancy function
$(window).on('scroll', function() {
  if (isScrolledIntoView('.animation_container')) {
    doSomeComplicatedStuff();
    $(window).unbind('scroll');
  }
});
发布评论

评论列表(0)

  1. 暂无评论