什么会导致 jQuery 的 unbind 功能无法按预期工作?

发布于 2024-11-11 18:11:48 字数 3206 浏览 4 评论 0原文

看一下下面的代码(此外,您将需要 jquery.js, jquery.viewport.jsjquery.scrollTo.js)。

我期望此脚本的行为是,每当我滚动页面时,红色行(具有 alwaysVisible 类的 元素)应插入到顶部下方- 该表最可见的行( 元素)。然后,应该滚动页面,以便这些红色行中的第一行“恰好”出现在视口的顶部。实际发生的情况是,makeVisibleWhatMust(); 被重复调用,直到到达页面末尾。我认为 $(window).unbind('scroll'); 会阻止 makeVisibleWhatMust(); 再次被调用,但显然这不起作用。

有什么想法吗?

这是我编写的 JavaScript:

function makeVisibleWhatMust()
{
  $('#testContainer').text( $('#testContainer').text() + 'called\n');
  $('table.scrollTable').each
  (
    function()
    {
      var table = this;
      $($('tr.alwaysVisible', table).get().reverse()).each
      (
    function()
    {
      $(this).insertAfter( $('tr:in-viewport:not(.alwaysVisible)', table)[0] );
    }
      );
      $(window).unbind('scroll');
      $(window).scrollTo( $('tr.alwaysVisible')[0] );
      $(window).bind('scroll', makeVisibleWhatMust);
    }
  );
}

$(document).ready
(
  function()
  {
    $(window).bind('scroll', makeVisibleWhatMust);
  }
);

这是一个用于测试它的 HTML 页面:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <title>Scroll Tables Test Page</title>

    <script type="text/javascript" src="jQuery.js"></script>
    <script type="text/javascript" src="jquery.viewport.js"></script>
    <script type="text/javascript" src="jquery.scrollTo.js"></script>
    <script type="text/javascript" src="scrollTable.js"></script>

    <style type="text/css">
      table th, table td
      {
    border: 1px solid #000;
    padding: .3em;
      }
      .alwaysVisible
      {
    background: #F66;
      }
    </style>
  </head>
  <body>
    <table class="scrollTable">
      <thead>
    <tr class="alwaysVisible">
      <th>Row name</th>
      <th>Content</th>
    </tr>
    <tr class="alwaysVisible">
      <th>Row 2</th>
      <th>Row 2</th>
    </tr>
      </thead>
      <tbody>
    <script type="text/javascript">
      for(var i = 0; i < 50; ++i)
      {
        document.writeln("<tr><td>Row " + i + "</td><td>Content</td></tr>");
      }
    </script>
      </tbody>
      <tfoot>
    <tr>
      <td>Footer</td>
      <td>Footer 2</td>
    </tr>
      </tfoot>
    </table>
    <div id="testContainer">TEST CONTAINER</div>
  </body>
</html>

Have a look at the following code (additionally, you will need jquery.js, jquery.viewport.js and jquery.scrollTo.js).

The behaviour I would expect from this script is that whenever I scroll the page, the red rows (<tr> elements with class alwaysVisible) should be inserted just underneath the top-most visible row (<tr> element) of that table. Then, the page should be scrolled so that the first of these red rows appears "exactly" at the top of the viewport. What actually happens is that makeVisibleWhatMust(); is called repeatedly until I reach the end of the page. I thought $(window).unbind('scroll'); would keep makeVisibleWhatMust(); from being called again, but apparently this doesn't work.

Any ideas why?

Here is the JavaScript I wrote:

function makeVisibleWhatMust()
{
  $('#testContainer').text( $('#testContainer').text() + 'called\n');
  $('table.scrollTable').each
  (
    function()
    {
      var table = this;
      $($('tr.alwaysVisible', table).get().reverse()).each
      (
    function()
    {
      $(this).insertAfter( $('tr:in-viewport:not(.alwaysVisible)', table)[0] );
    }
      );
      $(window).unbind('scroll');
      $(window).scrollTo( $('tr.alwaysVisible')[0] );
      $(window).bind('scroll', makeVisibleWhatMust);
    }
  );
}

$(document).ready
(
  function()
  {
    $(window).bind('scroll', makeVisibleWhatMust);
  }
);

And here is an HTML page to test it on:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <title>Scroll Tables Test Page</title>

    <script type="text/javascript" src="jQuery.js"></script>
    <script type="text/javascript" src="jquery.viewport.js"></script>
    <script type="text/javascript" src="jquery.scrollTo.js"></script>
    <script type="text/javascript" src="scrollTable.js"></script>

    <style type="text/css">
      table th, table td
      {
    border: 1px solid #000;
    padding: .3em;
      }
      .alwaysVisible
      {
    background: #F66;
      }
    </style>
  </head>
  <body>
    <table class="scrollTable">
      <thead>
    <tr class="alwaysVisible">
      <th>Row name</th>
      <th>Content</th>
    </tr>
    <tr class="alwaysVisible">
      <th>Row 2</th>
      <th>Row 2</th>
    </tr>
      </thead>
      <tbody>
    <script type="text/javascript">
      for(var i = 0; i < 50; ++i)
      {
        document.writeln("<tr><td>Row " + i + "</td><td>Content</td></tr>");
      }
    </script>
      </tbody>
      <tfoot>
    <tr>
      <td>Footer</td>
      <td>Footer 2</td>
    </tr>
      </tfoot>
    </table>
    <div id="testContainer">TEST CONTAINER</div>
  </body>
</html>

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

终难遇 2024-11-18 18:11:48

我认为你的问题是 scrollTo 使用animate

// From the plugin's source
function animate( callback ){  
    $elem.animate( attr, duration, settings.easing, callback && function(){  
        callback.call(this, target, settings);  
    });  
};

并且 animate 使用计时器来执行动画。结果是 .scrollTo 将在滚动完成之前返回,并且您将在 scrollTo 仍在滚动时重新绑定滚动处理程序。因此,就会发生你意想不到的事件。

一个简单的解决方案是使用一个标志来告诉 makeVisibleWhatMust scrollTo 正在滚动,并在完成时使用 scrollTo 回调来清除该标志,像这样:

function makeVisibleWhatMust() {
  // Ignore the event if we're doing the scrolling.
  if(makeVisibleWhatMust.isScrolling)
    return;
  $('#testContainer').text( $('#testContainer').text() + 'called\n');
  $('table.scrollTable').each(function() {
      var table = this;
      $($('tr.alwaysVisible', table).get().reverse()).each(function() {
        $(this).insertAfter( $('tr:in-viewport:not(.alwaysVisible)', table)[0] );
      });
      makeVisibleWhatMust.isScrolling = true;
      $(window).scrollTo($('tr.alwaysVisible')[0], {
        onAfter: function() { makeVisibleWhatMust.isScrolling = false; }
      });
    }
  );
}
makeVisibleWhatMust.isScrolling = false;

这是一个似乎可以工作的实时版本: http://jsfiddle.net/ambiguously/ZEx6M/1/

I think you're problem is that scrollTo uses animate:

// From the plugin's source
function animate( callback ){  
    $elem.animate( attr, duration, settings.easing, callback && function(){  
        callback.call(this, target, settings);  
    });  
};

And animate uses a timer to perform the animation. The result is that .scrollTo will return before the scrolling has completed and you'll rebind your scroll handler while the scrollTo is still scrolling. Hence the events when you're not expecting them.

An easy solution would be to use a flag to tell makeVisibleWhatMust that scrollTo is scrolling and use a scrollTo callback to clear the flag when it is done, something like this:

function makeVisibleWhatMust() {
  // Ignore the event if we're doing the scrolling.
  if(makeVisibleWhatMust.isScrolling)
    return;
  $('#testContainer').text( $('#testContainer').text() + 'called\n');
  $('table.scrollTable').each(function() {
      var table = this;
      $($('tr.alwaysVisible', table).get().reverse()).each(function() {
        $(this).insertAfter( $('tr:in-viewport:not(.alwaysVisible)', table)[0] );
      });
      makeVisibleWhatMust.isScrolling = true;
      $(window).scrollTo($('tr.alwaysVisible')[0], {
        onAfter: function() { makeVisibleWhatMust.isScrolling = false; }
      });
    }
  );
}
makeVisibleWhatMust.isScrolling = false;

And here's a live version that seems to work: http://jsfiddle.net/ambiguous/ZEx6M/1/

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文