【问题标题】:Ajax request succeeds but continues on to the target url regardlessAjax 请求成功,但无论如何都会继续到目标 url
【发布时间】:2014-05-01 16:00:53
【问题描述】:

我正在处理一个页面,该页面包含 div 中的各种 Youtube 视频缩略图,其中每个 <img> 都附加了链接,并且容器上有 Masonry 来控制类切换以调整所选视频的大小。

这个想法是点击div,这会触发masonry来更改CSS,还会触发一个.ajax()对django的请求,它会返回所选视频的模板。 (当您再次选择它以返回缩略图模板并使用 Masonry 重置为正常时,将执行相反的操作。

目前我有两个 javascript 函数,一个在 div 上触发砌体,另一个在视频链接上触发 ajax 请求;

<script>
(function($) {
    $(document).ready(function() {
        var container = $('.masonry'), masonry;
        //masonry = new Masonry(container[0]);
        container.masonry('stamp', container.find('.stamp'));

        container.unbind('click').on('click', '.item', function() {
            var $this = $(this),
                this_link = $this.find('a'),
                $this_link = $(this_link),
                this_href = $this_link.attr('href'),
                video_id = $this.attr('data-video-id'),
                gigante = $this.hasClass('gigante'),
                selector = 'div#panel-area.video_grid div.masonry div.item.' + video_id;

            $.ajax({
                type: "GET",
                async: false,
                url: this_href,
                timeout: 5000,
                data: {'g': gigante},
                dataType: 'html',
                success : function(data) {
                    $(selector).html(data);
                    container.find('.item').removeClass('gigante');
                    $(this).toggleClass('gigante', !gigante);
                    container.masonry();
                    console.log('Selector: ' + selector + '\nSuccess');
                }
            })
            .done(function(msg){
                console.log('Done: ' + msg);
            })
            .fail(function(jqXHR, textStatus){
                console.log('Failed: ' + textStatus);
            });

        });
    });
})(jQuery);    </script>

还有 HTML;

<div class="masonry js-masonry" data-masonry-options='{ "stamp": ".stamp", "isOriginLeft": false }'>
    <div class="mpus stamp">            
    </div>
    <div class="item video {{ object.id }}" data-video-id="{{ object.id }}">
        <a class="GetYoutubeVideo" href="{% url 'show_video' video_id=object.id %}">
            <i class="icon-play-circled play"></i>

            <span class="title">
                {{ object.get_title|slice:":20" }}...<br/>
                {{ object.date_created|date:"d/m/Y" }}
            </span>
            {{ object.get_img_tag }}
        </a>
    </div>
</div>

我本质上是一个 javascript 新手,所以我确信这是一个非常基本的问题。当我通过禁用async 的chrome 开发工具运行此操作时,我看到ajax 请求返回了预期的内容,但最终最终出现在目标页面上,而不是按预期将内容加载到$(selector)。 当我启用async 时,它会立即失败。我已经阅读文档很长时间了,但我觉得我没有取得任何进展。任何帮助将不胜感激。

【问题讨论】:

    标签: javascript jquery ajax jquery-masonry masonry


    【解决方案1】:

    要避免默认点击操作,请按如下方式修改您的点击处理程序:

    .on('click', '.item', function(e) {
        var ...
    
        e.preventDefault();
    
        $.ajax({
    
            ...
        )}
    
        ...
    })
    

    e 添加到点击处理函数意味着事件详细信息在函数内可用 - 在e 对象上运行preventDefault() 方法,该方法可防止发生默认操作 - 例如,超链接不会更长的导航到它的目标。

    事件通常以以下方式发生,尽管这不是一个深入的总结:

    a 元素上发生点击事件。

    click 事件开始搜索事件处理程序。如果在导致的元素上找不到事件,则事件将在 DOM 树上“冒泡”,一次一层,直到它到达根 DOM 元素并且无法继续前进,或者找到点击处理程序。

    在任何时候,只要找到点击处理程序,就会执行点击处理程序中的代码。如果点击处理程序在事件对象上设置preventDefault(),或返回false,则不会采取进一步的行动。

    如果点击处理程序既没有返回false 也没有设置preventDefault(),那么除了您自己的事件处理程序之外,还将执行原始浏览器默认操作。

    修改后的完整代码:

    <script>
    (function($) {
        $(document).ready(function() {
            var container = $('.masonry'), masonry;
            //masonry = new Masonry(container[0]);
            container.masonry('stamp', container.find('.stamp'));
    
            container.unbind('click').on('click', '.item', function(e) {
                var $this = $(this),
                    this_link = $this.find('a'),
                    $this_link = $(this_link),
                    this_href = $this_link.attr('href'),
                    video_id = $this.attr('data-video-id'),
                    gigante = $this.hasClass('gigante'),
                    selector = 'div#panel-area.video_grid div.masonry div.item.' + video_id;
    
                e.preventDefault();
    
                $.ajax({
                    type: "GET",
                    async: false,
                    url: this_href,
                    timeout: 5000,
                    data: {'g': gigante},
                    dataType: 'html',
                    success : function(data) {
                        $(selector).html(data);
                        container.find('.item').removeClass('gigante');
                        $(this).toggleClass('gigante', !gigante);
                        container.masonry();
                        console.log('Selector: ' + selector + '\nSuccess');
                    }
                })
                .done(function(msg){
                    console.log('Done: ' + msg);
                })
                .fail(function(jqXHR, textStatus){
                    console.log('Failed: ' + textStatus);
                });
    
            });
        });
    })(jQuery);
    </script>
    

    【讨论】:

    • 由于.item 是链接的包装器,链接点击事件仍然会在包装div 之前首先被调用,不是吗?我从来没有 100% 确定事件冒泡或...(我忘记了相反的名字),但我认为事件通常会冒泡,而不是向下。
    • @Flater 那是我不确定的另一件事 - 对于事件处理程序,它们是否被子元素触发并因此被调用两次。我之前看到我的 Django 视图被 ajax 调用了两次。
    • 调用两次通常意味着您添加了两次事件监听器。 ...一般来说。不总是。只是经常发生:)
    • @Flater 是的,我认为当我将unbind 放在那里时它停止了。
    • 简单的jsfiddle来演示冒泡场景:jsfiddle.net/9Sd2g/1
    【解决方案2】:

    如果您不希望它实际直接转到该 URL,是否需要在您的 &lt;a&gt; 元素上设置有效的 href 属性?

    不是装聪明,只是问问有没有具体的原因。

    因为问题在于您的 &lt;a&gt; 元素仍在执行其“正常”职责,即更改页面。我可以看到您稍后通过 jQuery 检索该 URL,但这不重要。

    将您使用的属性更改为非 href:

    <a class="GetYoutubeVideo" href="#" data-custom-url="{% url 'show_video' video_id=object.id %}">
    

    当您需要检索值时,只需使用该新属性名称即可:

    this_href = $this_link.attr('data-custom-url')
    

    请注意,我不能 100% 确定您是否需要砌体的 href 属性。从您的代码示例中我可以看到,只要您能够从 an 属性 (不是特别是 href)中检索值,这并不重要。

    通过不使用 href 属性,您可以确保您的 &lt;a&gt; 元素在被点击时没有实际的 URL 可供参考。

    这是一个可接受的解决方案/解决方法吗?

    【讨论】:

    • 这就是我试图避免的解决方案。在我把它放在一起之前,我想要一个非 js 友好的网站。但老实说,我不知道这是否重要。
    【解决方案3】:

    您可以在点击事件回调中简单地返回 false:

    container.unbind('click').on('click', '.item', function() {
        ...
        #ajax
        ...
        return false;
    });
    

    我相信这是停止默认操作并防止事件冒泡的更实用的方法。

    【讨论】:

    • 我相信我以前见过,但不明白它的用途。谢谢。
    • return: false 等价于 e.preventDefault();e.stopPropagation();。我建议使用现代的、细粒度的方法来实现您的意图。
    猜你喜欢
    • 2011-07-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-07-21
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多