devote / HTML5-History-API

HTML5 History API expansion for browsers not supporting pushState, replaceState
http://spb-piksel.ru
MIT License
1.02k stars 182 forks source link

Fix handling anchors with nested elements #37

Closed sompylasar closed 10 years ago

sompylasar commented 10 years ago

If an anchor contains nested elements (e.g. a span), onAnchorClick did not handle the events that start on the nested elements.

Here is the jsfiddle for that case: http://jsfiddle.net/sompylasar/QNQqN/

IE8 seems not supported by jsfiddle so open the raw result in IE8 to avoid unrelated errors: http://jsfiddle.net/sompylasar/QNQqN/embedded/result/

Unfortunately, I could not include the latest version from GitHub directly, so I used the older one published at CDNJS.

devote commented 10 years ago

Very good! Thank you!

devote commented 10 years ago

@gentleone I can not understand exactly what you need. Give an example of code that you do not work or is not as it should. And we try to understand and help.

gentle-media commented 10 years ago

Hi Dimitri,

Thank you for your reply. If you go to the following URL you will see an image with a man with a weird moustache/beard and I have the same anchor link underneath it as underneath the little green document icon in that post, but for some reason the link on the image doesn't do anything.

Here's the URL, but you need the following login to access the page:

removed by gentleone

http://vimble.net/testsuite/aggenebbis/ [3]

Thanks in advance, Ralph


Gentle Media

T: +30 6941669905 E: ralph@gentlemedia.nl W: www.gentlemedia.nl Skype: ralphechter

Dmitrii Pakhtinov schreef op 10-10-2013 22:35:

@gentleone [1] I can not understand exactly what you need. Give an example of code that you do not work or is not as it should. And we try to understand and help.

Reply to this email directly or view it on GitHub [2].

Links:

[1] https://github.com/gentleone [2] https://github.com/devote/HTML5-History-API/pull/37#issuecomment-26089139 [3] http://vimble.net/testsuite/aggenebbis/

sompylasar commented 10 years ago

@gentleone You have a script that handles the click, prevents the default action and stops the event propagation by returning false from the handler (that is what jQuery does internally for you if you return false from an event handler).

    function actionAnchors( e ) {
        // load the desired content
        if (history.pushState) {
            this.href && loadContent( this.href, true );
            return false;
        }
    }

After you included devote/HTML5-History-API script, it defined history.pushState in all browsers, so the check always evaluates to true.

jQuery passes event.target as this into your handler. When clicking the image, event.target is the image and it does not have href, so only return false is executed.

You should get href from the anchor so using jQuery do:

var href = $(this).closest('a').attr('href');
href && loadContent( href, true );

In addition, you included dom.js which is unnessesary and even conflicting with jQuery because it replaces $ (see your console: Uncaught TypeError: Object #<dom> has no method 'on').

devote commented 10 years ago

Hi @gentleone, remove tag <script src="/testsuite/aggenebbis/js/dom.js"></script> from your project. The script you no need, because you're using jQuery.

@sompylasar,

When clicking the image, event.target is the image and it does not have href, so only return false is executed.

He does not use delegation, so event.target always has the correct value.

devote commented 10 years ago

@gentleone replace your the code in main.php to the new code below:

<?php
    header("Content-Type: application/javascript");
?>
$(function(){

    // keep a reference to the query processor, so you can stop a query if necessary
    var xhr;

    function loadContent(url, push) {

        // previous operation interrupt request
        if (xhr) xhr.abort();

        var
            fragmentsUrl = url.split("?"),
            reqUrl = fragmentsUrl.shift();

        // ask for new data
        xhr = $.ajax({
            url: "<?php echo _LINK_PATH;?>ajax/core.php" + (fragmentsUrl.length ? "?" + fragmentsUrl.join("?") : ""),
            data: {"action": "page", "url": reqUrl},
            type: "post",
            dataType: "json",
            success: function(data, textStatus, xhr) {
                if (data.status == 1) {

                    // change the content
                    $("#dynamic_content").html(data.page.template);

                    // iterate through all the tags with the name script
                    $("script").each(function(){

                        // looking for a modular script
                        if (/(.*)<?php echo str_replace('/', '\\/', _LINK_PATH );?>js\/jscore.php\?module(.*)$/i.test(this.src)) {
                            var parent = this.parentNode;

                            // remove the script found to free up memory
                            parent.removeChild( this );
                            // Create a new script
                            var script = document.createElement('script');
                            script.type = "text/javascript";
                            script.async = "async";
                            script.src = "<?php echo _LINK_PATH;?>js/jscore.php?module=" + data.page.module;

                            // load it for execution
                            parent.appendChild(script);

                            return false;
                        }
                    });

                    if (data.page.title) {
                        document.title = data.page.title;
                    }

                    if (push) {
                        // replace the link in your browser
                        history.pushState(null, null, url);
                    }
                }
            },
            complete: function(xhr, textStatus) {
                //alert(xhr.responseText);
            },
            error: function(xhr, textStatus) {
            }
        });
    }

    $(window).on('popstate', function(e){
        /*
        * mind you, this is the only difference when working with the library,
        * Because the object document.location not be rebooted, so
        * History library returns generated by "location" object inside
        * Object window.history, so get it out of the "history.location".
        * For browsers support "history.pushState" get
        * Shaped object "location" with the usual "document.location".
        */
        var loc = history.location || document.location;
        // load the desired content
        loadContent(loc.href);
    });

    $(document).on('click', 'a.ajax', function(e) {
        if (window.history.pushState) {
            var href = $(e.target).closest('a').attr('href');
            href && loadContent(href, true);
            e.preventDefault();
        }
    });

    // if the link has a class of 'external' means to open it in another browser tab
    $(document).on('click', 'a.external', function(e) {
        var href = $(e.target).closest('a').attr('href');
        href && window.open(this.href);
        e.preventDefault();
    });
});
sompylasar commented 10 years ago

He does not use delegation, so event.target always has the correct value.

Sorry, I meant this value which I've checked in the debugger.

gentle-media commented 10 years ago

Hi Dimitri,

Thanks you so much for looking into this and sending me the solution to my issue. I see where you made the changes with .closest and it makes perfect sense to me. Once again apologies for asking for help on the Github pages. I know it's not the place to ask for these kind of things, but when I saw that commit I thought that's my issue while actually it was not. :) You mentioned earlier to remove dom.js because I use jQuery already and I was indeed doubting myself about this. I know that jQuery has its own selector engine, Sizzle if I'm right, and dom.js does the same thing , but I was not sure. Thanks again!

Kind regards, Ralph


Gentle Media

T: +30 6941669905 E: ralph@gentlemedia.nl W: www.gentlemedia.nl Skype: ralphechter

Dmitrii Pakhtinov schreef op 11-10-2013 12:57:

@gentleone [1] replace your the code in main.php to the new code below:

<?php header("Content-Type: application/javascript"); ?> $(function(){

// keep a reference to the query processor, so you can stop a query if necessary var xhr;

function loadContent(url, push) {

// previous operation interrupt request if (xhr) xhr.abort();

var fragmentsUrl = url.split("?"), reqUrl = fragmentsUrl.shift();

// ask for new data xhr = $.ajax({ url: "<?php echo _LINK_PATH;?>ajax/core.php" + (fragmentsUrl.length ? "?" + fragmentsUrl.join("?") : ""), data: {"action": "page", "url": reqUrl}, type: "post", dataType: "json", success: function(data, textStatus, xhr) { if (data.status == 1) {

// change the content $("#dynamic_content").html(data.page.template);

// iterate through all the tags with the name script $("script").each(function(){

// looking for a modular script if (/(._)<?php echo str_replace('/', '\/', _LINKPATH );?>js/jscore.php?module(.)$/i.test(this.src)) { var parent = this.parentNode;

// remove the script found to free up memory parent.removeChild( this ); // Create a new script var script = document.createElement('script'); script.type = "text/javascript"; script.async = "async"; script.src = "<?php echo _LINK_PATH;?>js/jscore.php?module=" + data.page.module;

// load it for execution parent.appendChild(script);

return false; } });

if (data.page.title) { document.title = data.page.title; }

if (push) { // replace the link in your browser history.pushState(null, null, url); } } }, complete: function(xhr, textStatus) { //alert(xhr.responseText); }, error: function(xhr, textStatus) { } }); }

$(window).on('popstate', function(e){ /*

  • mind you, this is the only difference when working with the library,
  • Because the object document.location not be rebooted, so
  • History library returns generated by "location" object inside
  • Object window.history, so get it out of the "history.location".
  • For browsers support "history.pushState" get
  • Shaped object "location" with the usual "document.location". */ var loc = history.location || document.location; // load the desired content loadContent(loc.href); });

$(document).on('click', 'a.ajax', function(e) { if (window.history.pushState) { var href = $(e.target).closest('a').attr('href'); href && loadContent(href, true); e.preventDefault(); } });

// if the link has a class of 'external' means to open it in another browser tab $(document).on('click', 'a.external', function(e) { var href = $(e.target).closest('a').attr('href'); href && window.open(this.href); e.preventDefault(); }); });

Reply to this email directly or view it on GitHub [2].

Links:

[1] https://github.com/gentleone [2] https://github.com/devote/HTML5-History-API/pull/37#issuecomment-26129036