自从我发布了文章 动态页面/替换内容 以来,我收到了很多邮件,这些邮件来自尝试将其与其他一些 JavaScript 代码一起使用并遇到问题的人。大多数情况下,这是一种某种形式的灯箱效果。他们的其中一个页面上有一堆缩略图,当他们在其中加载该页面时,灯箱效果不起作用。
问题在于,当缩略图加载到页面上时(通过 Ajax,即 jQuery 的 .load() 函数),它们没有任何事件绑定到它们。
/* Your lightbox plugin */
$("photos a").ceebox();
/* Basics of Ajax */
$("nav a").click(function(e) {
e.preventDefault();
$("#main-content").load(this.href); /* Thumbnails loaded from here */
});
灯箱插件(可能)的工作方式是,当页面加载时,它会将点击事件绑定到您在该选择器(缩略图)中传递的元素,并且这些点击事件会执行灯箱操作。由于新加载的缩略图没有点击事件,因此灯箱操作不起作用。
一种解决方法是在内容加载后,在 load 函数的回调函数中调用灯箱插件
$("photos a").ceebox();
$("nav a").click(function(e) {
e.preventDefault();
$("#main-content").load(this.href, function() {
/* Callback function */
$("photos a").ceebox(); /* Call this again */
});
});
有点重复,但这可以解决问题。
使用委托的更好方法
虽然这是“JavaScript 的工作方式”,但它确实让人很头疼。由于 jQuery 是一个旨在缓解此类问题的库,当然它有更好的方法。这种方法是 .delegate() 函数,它不是将事件绑定到各个元素,而是将事件绑定到 DOM 树中较高层次的元素,该元素不太可能通过 Ajax 替换,并且会监视这些点击事件。
这依赖于称为事件冒泡的东西,这是 JavaScript 中一个巧妙且重要的概念(实际上:DOM 模型)。如果您点击缩略图,它将触发该元素上的点击事件,然后触发其父元素上的点击事件,以及其父元素的父元素上的点击事件,一直到根元素。因此,我们可以从上层元素监视更深层元素上的点击事件。
不幸的是,在我们的灯箱示例中,您必须更改插件本身才能使其使用委托而不是直接绑定到元素。绝对可以做到,但比较棘手,因为您可能不像处理自己的代码那样熟悉该插件的代码。
听听 Remy Sharp 的讲解
在撰写本文时,Remy Sharp 发布了一个关于此确切主题的视频屏幕录制。他比我更擅长解释它,所以请 观看该视频。
我认为解释$(…).live();和$(…).delegate();之间的区别将是本文的下一步好步骤。因为您可以使用…
做同样的事情。
解释 Delegate 和 Live 的区别对大多数人都有帮助。
— 只是一个想法
我非常确定区别在于 .live() 会自动将事件绑定到文档。这意味着事件需要一直冒泡到那里才能得到处理。这不如委托有效,在委托中,您只需绑定到您需要的尽可能高的元素。
如果我错了,请随时纠正我。
Chris 说得对。.delegate() 函数效率更高。
此外,您无法在 .live() 中使用链式操作,这可能很麻烦。
从 1.4 版本开始,您可以强制 live 在某个元素处停止,尽管查看代码后发现它比 delegate 复杂得多。
但是,委托的另一个优势是可以链接选择器,这在限制事件范围方面提供了一些灵活性。
@chris
我不明白。无论是否附加了事件监听器,事件都会冒泡。在链中较低位置附加事件监听器如何提高性能?
我唯一能想到的是(如果我错了,请打我),由于 live 绑定到根元素,它会监听页面上的每次点击,然后检查目标元素是否为选择器中指定的元素。在我看来,这是性能下降的最大原因。对于 $(‘#whatever’).live(‘hover’, function…, 情况更糟,因为它会在您在页面上移动的任何地方持续监听!!!
如果我在这方面有任何错误,请有人纠正我…
我认为……当事件在文档中绑定时,它必须向下检查每一层,以找到它应该响应的元素。因为显然文档上的每次点击都不会触发事件,您只希望它在点击您提供的元素内部时触发。所有这些递归检查导致效率降低。
@chris
我这样理解(再说一遍,我并没有查看代码,只是我这样认为)
当您使用 live/delegate 时,它在幕后所做的是将事件监听器附加到指定的祖先元素(或根元素),类似于以下内容(假设您使用的是 ID 选择器。任何其他选择器,它都会变得更复杂)
$('#ancestor').click(function(e){
if(e.target === $('#descendant')[0]) {
// 执行您的匿名函数
}
});
因此无需查找该元素。
再说一遍,这不是我从代码中提取的,只是我的假设。
如果我错了,请纠正我…
@joseph 也许您可以在谷歌上花 5 分钟时间自己回答这个问题?
http://www.alfajango.com/blog/the-difference-between-jquerys-bind-live-and-delegate/
给出了非常好的解释。
您可以使用 delegate 做的事情类似于
如果我没有完全弄错,当“nav”元素加载到您的页面中时,它将触发 load 事件,然后在其中您可以调用您的灯箱或任何插件。(我无法完全测试这段代码,但是
另一种方法是在您的 AJAX 回调函数中调用它,这样您也可以确保在调用插件之前所有 DOM 都已加载并且所有图像都已就绪。
无论如何,Chris,好文章!
我现在没有测试过(从我的 iPhone 发布),但我认为‘load’事件不会冒泡…
似乎无法解决此问题:我想使用 jQuery onClick 函数将 class=”display” 的实例更改为 class=”displaynone”,当用户点击“next”或“previous”时。我尝试遵循本文中提到的内容,但对我来说意义不大。任何建议都将不胜感激。
我的脚本是
我的主体标记是
我尝试使用 .mousedown,因为它可能没有被灯箱绑定。没有用。我尝试了以下操作
这也没有用。我真的不知道该怎么办
:(
Css-Tricks 社区,我需要你们的帮助!
JavaScript 不是 jQuery。
此问题与所有 JavaScript 相关,无论库如何。jQuery 仅用作示例和解决方案。
我尝试了 5 个小时来理解这个 JavaScript。
我有一些问题,希望你能按顺序回答,如果可以的话,请首先帮我解决。
我使用以下方式运行脚本:
在我的页面头部部分。
我还通过以下方式使用动态页面的脚本:
我应该在哪里放置委托才能使灯箱工作?我尝试在 dynamic page.js 中,在 $(“nav”).delegate(“a”, “click”, function() { 之后放置。
但没有任何反应。我还尝试在此之前放置 $(‘#gallery a’).lightBox();,以及在此之后像第一个示例一样放置,但仍然没有任何反应。我看了视频,并且理解了委托的工作原理。
请帮我解释一下,我无法使其工作,我尝试了所有方法,并且反复观看视频 5 次试图理解,但仍然无法解决。非常感谢。
我还尝试了第一种方法,但我找不到你在 dynamic page.js 文件中提到的代码部分,以便放置 $(‘#gallery a’).lightBox();。它在单独的文件中,这是问题所在吗?我不这么认为。发生了什么事?我尝试使用委托更改插件灯箱,使所有绑定都使用委托,但没有任何反应……还有什么可以尝试的?
确保在初始化灯箱之前 DOM 已加载:如果你在 HEAD 标签内运行代码,请尝试将其移动到页面末尾,就在