Question

I'm working on a page which contains various Youtube video thumbnails in divs which have links attached to each <img> and has Masonry on the container to control class switching to resize selected videos.

The idea is that the div is clicked on, which triggers masonry to change the CSS and also triggers an .ajax() request to django which returns the template for the selected video. (And will perform the opposite when you select it again to return the thumbnail template and reset to normal with Masonry).

As it stands I have two javascript functions, one triggering masonry on the div, and then another triggering the ajax request on the video link;

<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>

And the 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>

I'm a javascript novice essentially so I'm sure this is a very basic issue. When I run this through chrome dev tools with async disabled I see the ajax request return the expected content, but then ultimately end up on the target page instead of loading the content in to $(selector) as expected. When I enable async it just instantly fails. I've been reading docs for ages but don't feel like I'm getting anywhere. Any help would be appreciated.

Was it helpful?

Solution

To avoid the default click action, modify your click handler as follows:

.on('click', '.item', function(e) {
    var ...

    e.preventDefault();

    $.ajax({

        ...
    )}

    ...
})

Adding e to the click handler function means that the event details are available within the function - on the e object you run the preventDefault() method which prevents default actions from occuring - for instance, a hyperlink will no longer navigate to its target.

The event occurs in generally the following manner, though this is don't an in-depth summation:

A click event occurs on the a element.

The click event starts a search for an event handler. Should one not be found on the element that caused then the event will 'bubble' up the DOM tree, one level at a time until it either reaches the root DOM element and cannot go further, or a click handler is found.

At any point, should a click handler be found then the code in the click handler is executed. If the click handler sets preventDefault() on the event object, or returns false then no further action is taken.

If the click handler neither returns false nor sets preventDefault() then the original browser default action will be executed in addition to your own event handler.

Your code in full with modifications:

<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>

OTHER TIPS

Is there a point to setting a functioning href attribute on your <a> element if you don't want it to actually go to that URL directly?

Not trying to be clever, just asking if there is a specific reason for it.

Because the problem is that your <a> element is still performing its 'normal' duties, i.e. changing the page. I can see you retrieve that URL via jQuery at a later stage, but that shouldn't matter.

Change your used attribute to something that is NOT href:

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

And when you need to retrieve the value, just use that new attribute name:

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

Note that I'm not 100% sure if you need the href attribute for masonry. From what I can see from your code examples, it doesn't matter, as long as you're able to retrieve the value from an attribute (not specifically href).

By NOT using the href attribute, you're making sure your <a> element doesn't have an actual URL to refer to when it gets clicked.

Is this an acceptable solution/workaround?

You could simply return false in your click event callback:

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

I believe it's a more practical way of stopping the default action and preventing the event from bubbling up.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top