【问题标题】:shadowbox stops working after jquery function calljquery函数调用后shadowbox停止工作
【发布时间】:2012-02-04 21:11:19
【问题描述】:

我有一个影子盒脚本。当我加载页面时一切正常,但是当我调用这个 jquery 加载函数然后尝试通过单击图像来触发阴影框时,大图像会在新窗口中打开。 代码如下:

<link href="CSS/main.css" rel="stylesheet" type="text/css" />
<script type="text/javascript"  src="shadowbox-3.0.3/shadowbox.js"></script>
<script type="text/javascript">
Shadowbox.init();
</script>

<p id="compas"><a href="images/coverage.jpg" rel="shadowbox" title="Coverage map"></a></p>

知道为什么会这样吗?

【问题讨论】:

  • 错误控制台(Firebug、Chrome 控制台)报告什么?您可能还想使用 Persist。
  • 没有显示。图像在新的普通窗口中打开。
  • 我所看到的,我没有发现任何问题本身。有链接吗?
  • 没有链接,但我可以把代码发给你吗?或者我可以在这里添加jquery函数。
  • 如果可以,请创建jsfiddle.net

标签: javascript jquery shadowbox


【解决方案1】:

编辑

所以,我们终于弄明白了。在首次评论此问题 15 小时后,以及至少 50 次迭代之后,我们终于确定了问题所在以及如何解决它。

当我在我的服务器上创建本地 aaa.htmlbbb.html 时,我突然感到震惊。就在那时,当$.load() 运行回调函数时,被替换内容的元素节点被完全从 DOM 中删除。因此,一旦 #menu-home 内容元素被替换,它们就会从 DOM 中移除,并且不再应用 Shadowbox。

一旦我弄清楚了,这只是一个网络搜索的问题,我发现:

Nabble-Shadowbox - Reinit Shadowbox

具体来说,来自mjijackson 的回复。他描述的是如何使用以下方法“重启”(重新初始化)Shadowbox:

Shadowbox.clearCache();
Shadowbox.setup();

所以一旦#menu-home 内容被重新加载,需要清除Shadowbox 缓存(本质上是在页面上关闭它),然后运行Shadowbox.setup(),它将检测所有元素再次。您也不会再次运行Shadowbox.init() 方法。

我注意到您曾尝试将Shadowbox.setup() 复制/粘贴到$.load() 之后,至少在代码中按顺序复制/粘贴。但是,这不会起作用,因为需要首先清除缓存,主要是因为.clearCache().setup() 函数需要在$.load() 完成之后运行 (完成并运行任何回调)。这两个函数需要在$.load() 回调处理程序中运行;否则,您会立即运行它,但 $.load() 是异步的,将在稍后完成。

我将回顾我所做的其他一些更改,以便您了解是什么、为什么以及为什么。

注意,我不确定你是否熟悉&lt;base&gt;,但下面是HEAD 元素的顶部:

<base href="http://62.162.170.125/"/>

这只是让我使用您计算机上的资源文件。您很可能不想在您的实际网站上使用它。如果您复制/粘贴,请确保并删除此行。

<div id="menu">
  <ul>
    <li><a id="menu-home" href="index.html" rel="http://jfcoder.com/test/homecontent.html">Home</a></li>
    <li><a id="menu-services" href="services.html" rel="http://jfcoder.com/test/servicescontent.html">Services</a></li>
    <li><a id="menu-tour" href="tour.html" rel="http://jfcoder.com/test/tourcontent.html">Tour</a></li>
    <li><a id="menulogin" href="login.html">Login</a></li>
  </ul>
</div>

在这里,您会注意到我在 HREF 属性中有一个相对 url,以及一个指向我服务器上某些页面的链接。链接到我的服务器的原因是由于跨站点脚本限制,我无法通过 AJAX 访问您的 aaa.htmlbbb.html 文件。指向我网站的链接也应删除。

现在,我在这里使用rel 属性的原因是我希望通过href 属性允许链接继续工作,以防JS 无法正常运行或存在其他问题错误。如果您有单独的文件,一个用于完整的 HTML 文档,另一个用于片段,这就是您想要做的。如果您可以从链接文件中同时提供完整文档和仅内容,那么您可能不需要rel 属性,但您需要管理请求以便服务器知道如何响应(完整文档或只是内容部分)。

var boxInitialize = function(){
    try {
        if (!Shadowbox.initialized) {
            Shadowbox.init();
            Shadowbox.initialized = true;
        } else {
            Shadowbox.clearCache();
            Shadowbox.setup();
        }
    } catch(e) {
        try {
            Shadowbox.init();
        } catch(e) {};
    }
};

我在这里所做的只是为初始化/设置请求创建一个中心位置。非常坦率的。请注意,我添加了 Shadowbox.initialized 属性,因此我可以跟踪 Shadowbox.init() 是否已运行,它只能运行一次。但是,如果可能,将所有内容集中在一个位置是一个好主意。

我还创建了一个可以作为常规函数调用的变量函数:

boxInitialize();

或者作为函数参考:

window.onload = boxInitialize; // Note, no () at the end, which execute the function

您可能会注意到我删除了$() 并用jQuery() 代替了它们。如果您最终得到一个包含多个框架和库竞争$() 的环境,这可能会变成一场真正的噩梦,因此最好避免它。前几天这真的让我很受打击。

由于我们在.ready() 回调中有一个闭包范围,我们可以利用它来保存几个“私有”变量,以便在脚本执行的不同时间使用。

var $ = jQuery,
    $content = jQuery("#content"), // This is "caching" the jQuery selected result
    view = '',
    detectcachedview = '',
    $fragment,
    s = Object.prototype.toString,
    init;

注意除最后一行之外的所有末尾的,。看看我是如何通过使$ 等于 jQuery 变量来“导入”它的,这意味着您实际上可以在其中使用它#。

var loadCallback = function(response, status, xhr){
    if (init != '' && s.call(init) == '[object Function]') {
        boxInitialize();
    }

    if (xhr.success() 
          && view != '' 
            && typeof view == 'string' 
              && view.length > 1) {
        $fragment = $content.clone(true, true);
        cacheContent(view, $fragment);
    }
};

这在$.load() 完成AJAX 请求的过程时运行。请注意,请求中返回的内容在运行时已经放置在 DOM 上。另请注意,我们将实际缓存的内容存储在$content.data() 中,永远不应将其从页面中删除;只有它下面的内容。

var cacheContent = function(key, $data){
    if (typeof key == 'string'
          && key.length > 1
            && $data instanceof jQuery) {
        $content.data(key, $data.html());
        $content.data(detectcachedview, true);
    }
};

cacheContent() 是一种你可能不想要的方法;本质上,如果它已经在先前的请求中加载,那么它将被缓存然后直接检索,而不是启动另一个 $.load() 从服务器获取内容。您可能不想这样做;如果是这样,只需将 menuLoadContent() 函数中的第二个 if 块注释掉即可。

var setContent = function(html){
    $content.empty().html(html);

    if (init != '' && s.call(init) == '[object Function]') {
        boxInitialize();
    }
};

这样做是首先清空其内容/元素的$content 元素,然后添加我们之前通过获取$content.html() 保存的指定的基于字符串的标记。这是我们将在可能的情况下重新添加的内容;您可以看到,一旦单击并加载了不同的链接,重新单击以重新显示该链接非常快。此外,如果它与当前加载的请求相同,它也会完全跳过运行代码。

(我们使用$content like 因为它是对包含 jQuery 元素的变量的引用。我这样做是因为它在闭包范围内,这意味着它不会出现在全局范围内,但会可用于事件处理程序等。

在代码中查找内联 cmets。

var menuLoadContent = function(){
    // This is where I cancel the request; we're going to show the same thing
    // again, so why not just cancel?
    if (view == this.id || !this.rel) {
        return false;
    }

    // I use this in setContent() and loadCallback() functions to detect if
    // the Shadowbox needs to be cleared and re-setup. This and code below
    // resolve the issue you were having with the compass functionality.
    init = this.id == 'menu-home' ? boxInitialize : '';
    view = this.id;
    detectcachedview = "__" + view;

    // This is what blocks the superfluous $.load() calls for content that's
    // already been cached.
    if ($content.data(detectcachedview) === true) {
        setContent($content.data(view));
        return false;
    }

    // Now I have this in two different spots; there's also one up in 
    // loadCallback(). Why? Because I want to cache the content that
    // loaded on the initial page view, so if you try to go back to
    // it, you'll just pickup what was sent with the full document.
    // Also note I'm cloning $content, and then get it's .html() 
    // in cacheContent().
    $fragment = $content.clone(true, true);
    cacheContent(view, $fragment);

    // See how I use the loadCallback as a function reference, and omit
    // the () so it's not called immediately?
    $content.load(this.rel, loadCallback);

    // These return false's in this function block the link from navigating
    // to it's href URL.
    return false;
};

现在,我选择不同的相关菜单项。您不需要为每个元素单独声明$.click();相反,我选择了#menu a[rel],这将获得菜单中具有rel="not empty rel attribute" 的每个a 元素。再次注意我在这里如何使用menuLoadContent 作为函数引用。

jQuery("#menu a[rel]").click(menuLoadContent);

然后,在最底部,我运行 boxInitialize(); 来设置 Shadowbox。

如果您有任何问题,请告诉我。


我想我可能会追根究底。我认为缺陷在于您在单击菜单项时处理新内容的$.load() 的方式,以及我看到与iframe 有关的未捕获异常:

未捕获的异常:未知播放器 iframe

Nabble-Shadowbox forum thread 处理此错误。我实际上不再明白了,但我想它是我点击了tour 菜单项。

现在,您为菜单项加载内容所做的工作确实没有任何意义。您正在请求一个完整的 HTML 文档,然后只选择一个带有 class="content" 的元素。我可以看到这样做的唯一好处是页面永远不会重新加载,但是您需要采取另一种方法来获取和显示不涉及通过 AJAX 下载整个页面然后尝试让 jQuery 解析的数据只是你想要的部分。

我相信以这种方式处理内容加载是您问题的根本原因,因此$.load() 切换菜单视图会以意想不到的方式破坏您的页面。

问题:您为什么不直接链接到实际页面并跳过所有$.load() 的花哨?在速度方面,它不会产生太大的影响,如果有的话。像这样使用 AJAX 是没有意义的,因为您可以毫无问题地将它们链接到相同的内容。

有两种方法可以防止往返页面重新加载:

  1. 如果 URL 中有 ?contentonly=true 标志,则将 AJAX 调用设置为仅请求标记的 .content 部分,而不是整个 HTML 文档。这是传统上的完成方式,如果您有脚本环境,通常相对简单。

    $(".content").load('index.html?contentonly=true');
    

    然后您的服务器仅响应请求的内容视图。

  2. 在同一个 HTML 文档中提供所有内容视图,然后在适当的时候显示:

    var $content = $('.content');
    $content.find('.content-view').hide();
    $content.find('#services-content').show();
    

    您似乎没有很多内容要提供,因此初始页面加载可能不会对这种特定方法产生太大影响。您可能需要研究如何预加载图像,但这是一种众所周知的技术,有许多高质量的脚本和教程。

这些技术中的任何一种都可以使用#! (hashbang) 技术来加载内容,尽管我认为这对于搜索引擎来说存在一些问题。但是,这里有一个链接,指向我前段时间整理的一个简单技术:

http://jfcoder.com/test/hash.html

另外,这只是一个提示,但不要使用class 来引用您的“内容”元素,即.content。标记中应该只有一个内容显示元素,对吧?不止一个吗?使用id="content";这就是 ID 属性的用途,用于引用单个元素。 classes 旨在通过元素共享的某些特征对元素进行分组,所以上面当我 .hide() 内联内容视图(参见 #2)时,我查找所有相似的 class="content-view" 元素(它们包含内容视图标记)。但是$content 变量应该引用$('#content');。这是对元素是什么的描述。

【讨论】:

  • 我宁愿链接实际页面,但这不在要求中。客户端希望在不重新加载页面的情况下更改内容。我正在请求整个 HTML 页面并从中选择内容,因为我正在考虑将实际链接放在菜单项的 href 中,因此如果某些用户的浏览器不支持 javascript,它仍然可以工作。
  • AJAX 不会陷入类似链接的行为,它不会做任何事情(包括不触发整个页面加载)。您需要使用完整的 HTML 文档(常规的 GET 往返请求,如单击链接)或仅在 AJAX 请求期间显示的片段(内容部分)进行响应。您可以使用 AJAX 上的 GET 标志来完成此操作;只需省略 href 属性 URL 中的标志,这样它就会进入整页。然后,在服务器脚本上,检测您是需要响应完整的 HTML 文档还是仅响应内容。
  • 在 Firebug 或 Chrome 的 Net 控制台打开的情况下查看 this demo,当请求页面片段时,查看响应文本是什么。您会看到它只是一个 HTML 片段,而不是完整的文档。
  • 另请注意,OP(在这种情况下是您)有责任审查所提供的答案,并在适当的情况下为任何好的答案投票并检查一 (1) 个答案作为接受的答案(它是答案旁边的勾勒复选标记,单击它以接受相邻的答案)。这就是你奖励那些花时间回答你问题的人的方式;点是“代表”。
  • 没问题。这是一个有趣的问题。我花了一段时间才弄清楚。
【解决方案2】:

这对我们有用,我们创建了一个使用垂直标签的网站,并使用 jQuery.load 调用带有阴影框图像的页面

只需将所有锚标记指定为 class="sbox" 并将此脚本粘贴到标题中即可。

<script>
    Shadowbox.init({
    skipSetup:true,
}); 
$(document).ready(function() {
    Shadowbox.setup($('.sbox'));//set up links with class of sbox
    $('a.sbox').live('click',function(e){
        Shadowbox.open(this);
        //Stops loading link
        e.preventDefault();
    });
});
</script>

注意:我们必须将 .sbox 类放在所有 rel="shadowbox" 锚点以及调用 .load 的选项卡的锚点上

感谢这个人-> http://www.webmuse.co.uk/blog/shadowbox-ajax-and-other-generated-content-with-jquery-and-javascript/

【讨论】:

    【解决方案3】:

    嗯,根据 Shem 的回答,这是我的解决方案。 每次点击特定类,设置并打开来自同一类的元素的 shadowbox:

    jQuery('.sb-gallery a').live('click',function(e){
        Shadowbox.setup(jQuery('.sb-gallery a'));
        Shadowbox.open(this);
        //Stops loading link
        e.preventDefault();
    });
    

    谢谢大家

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-08-24
      • 1970-01-01
      • 1970-01-01
      • 2011-10-16
      • 1970-01-01
      • 2023-03-28
      • 2016-02-16
      • 2015-10-04
      相关资源
      最近更新 更多