Link to home
Start Free TrialLog in
Avatar of Dante Gagliardi
Dante GagliardiFlag for United States of America

asked on

How to properly bubble events to create new window/tab

The setup:

My page consists of elements (anchors) that need to make an AJAX call when they are clicked. However, when the elements are left-clicked, the page loads and interrupts the AJAX call. I do not have the ability to modify this AJAX call, and so I cannot just make it synchronous.

To fix this problem, I created an event handler that would capture click events, and use another method that would load the target page after completion of the AJAX call. This also required that I do not bubble the event, lest I interrupt the AJAX call again. However, this broke the ability to middle click/ctrl click/shift click and open the page in a new tab/window.

To fix that problem, I modified the event handler to act on the mouseup event (because Firefox triggers mouseup on middle click). If it is a mouseup event from the left mouse button, with no modifier keys, I use the above mentioned method to load the target page after the AJAX call completes. If the event has a modifier key, or is a middle click, I do nothing and allow the event to bubble (if the target is opened in a new tab/window, the AJAX call will continue processing just fine).

Unfortunately, that gets me here. What is happening now, is the mouseup event triggers one page load, while click event fires immediately afterwards (which I no longer handle) that appears to be firing a second page load. This is doubling my server overhead for these links.

This handler is also bound to the keypress event, so we can capture <Enter> keypresses.

Below is the Handler I am using (any syntax errors are likely from simplifying the handler code):
// This handler is bound to the 'mouseup' and 'keypress' events
function (e){
    // Ignore anything that isn't left click, middle click, or <Enter>
    if(e.which != 1 && e.which != 2 && e.which != 13){
        return true;
    }
    AJAX_CALL();
    // Bubble on modifier keys, or middle click
    if(e.metaKey || e.ctrlKey || e.shiftKey || e.which == 2){
        return true;
    }
    // Else, make sure this element has an href, and follow it with our custom method
    else if(this.attr('href') != '#' && this.attr('href') !== undefined){
        goToUrl(this.attr('href'),false);
        return false;
    }
}

Open in new window


What I'm looking for:

The problem boils down to this: I need to be able to issue an asynchronous AJAX call that isn't interrupted, but the handler that does this must retain default browser behavior for opening new tabs/windows. This solution needs to work on Chrome/Safari/Firefox/IE8+, and preferably also on mobile devices (though mobile isn't a strict requirement).

Ideally, I would like a pure client-side solution (using JS and jQuery 1.9.1).

My investigation thus-far suggests that this isn't a trivial problem to solve on a cross-browser basis. The functionality I am trying to create is important enough that a pre-existing library capable of doing this is not out of the question, but it is not preferred.

I've run up against a wall on this one. Any help would be greatly appreciated.
Avatar of Gary
Gary
Flag of Ireland image

Not sure if I am following you but

$(".somelink").click(function(e){
        e.preventDefault() // stop default action
        $.ajax({
            ...,
            success: function () {
                window.open($(this).prop("href"));
               }
        });
})
Avatar of Dante Gagliardi

ASKER

Gary,

The problem with that, is that I don't want to prevent the default action, I merely want to delay it if the action will cause a page load in the current tab. If the action would result in a new tab/window, I can let the default continue without pause. Unfortunately, I don't have control over the nature of the AJAX call (otherwise I could just make it synchronous), preventing me from adding a success function.
That will delay it until the ajax has finished.
You can amend it slightly if the target is _blank e.g.

$(".somelink").click(function(e){
	var attr = $(this).attr("target");
	if (typeof attr !== 'undefined' && attr !== false && attr!="_blank") {
		// either no target attribute or target is anything other than _blank
		e.preventDefault() // stop default action until ajax is finished
	}

        $.ajax({
            ...,
            success: function () {
                window.location = $(this).prop("href"); // change current location
               }
        });
        // original link will be executed if target is _blank even if the 
        // ajax call is still being executed.
}) 

Open in new window


If you give some real code I will probably understand you better as I am assuming these are links.
The handler is being bound to a simple anchor element. Your approach has two main problems:

One, I cannot modify the AJAX call in any way, so I cannot include a success function. If I could modify the AJAX call, it would be a simple matter of setting {async:false}. This is the purpose of the goToUrl(...) function, which provides the proper delay.

Two, this does not capture middle mouse click in Firefox. To do that, the handler must also fire on 'mouseup'. However, firing on 'mouseup' and 'click' causes the browser to follow the href of the anchor twice for some user events.

I'm not having an issue with making the async call, or with delaying the browser during the async call. My issue is that I am trying to retain the browser's default behavior during all of this.

Thank you for helping, btw. Hopefully we can figure this out.
Use mousedown to capture left,middle and right click, not mouseup.

If you cannot modify the ajax call then I am having a hard time understanding how you can manipulate it.
How is the ajax call being bound to the link?
The only way would be to bind on the click and in the event call make a call to the ajax code so it returns back to your click function.
The handler is the JS anonymous function provided above, bound by using jQuery's .on() function. The goToUrl(...) does the magic that delays the browser long enough for the AJAX call to complete.

I cannot use the goToUrl(...) function for every call, because there is a noticeable delay during the call, and this would be a serious disruption to the user experience. With that, I use it as sparingly as possible.

I can try the use of 'mousedown' and get back to you, but I suspect that I will still have the issue of the 'click' event being fired immediately afterwards, still leaving me with two page loads. I'll be back in a couple hours with results.
ASKER CERTIFIED SOLUTION
Avatar of Dante Gagliardi
Dante Gagliardi
Flag of United States of America image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
I ended up arriving at the solution independent of expert help. While appreciated, the help provided was not targeting the issue, and ignored key parts of the question.