32

I'm writing a website in which all content is stored in the JavaScript environment, so it should be possible to navigate between "pages" without additional HTTP requests.

I want to preserve the user's intent with respect to how links are opened, though. If a Mac user apple+clicks a link, for example, it should be opened in a new tab. But if the user wants to open a link in the current window, I want to avoid making a new HTTP request, and just use DOM manipulation to show the new content.

Here's what I tried (JSFiddle):

<a href="http://yahoo.com" id="mylink">Yahoo!</a>
document.getElementById("mylink").onclick = function() {
    alert("clicked");
    return false;
}

It behaves as desired for normal left clicks, and for context menu actions, but not for clicks with a modifier key. I could check whether certain modifier keys are held, but that seems fragile.

Twitter's website has behavior similar to what I want - when you click a username, it's normally an AJAX request, but if you click with a meta key held, you get a new tab.

I'd prefer a plain JavaScript solution without libraries, unless this requires a bunch of platform-specific logic.

Update

I took a look at GitHub's code, which also has the behavior I'm after, and it does check for certain keys being held. So I'm accepting Chris's answer , since it seems unlikely that there's a better alternative.

$(document).on("click", ".js-directory-link", function (t) {
    return 2 === t.which || t.metaKey || t.ctrlKey ? void 0 : ($(this).closest("tr").addClass("is-loading"), $(document.body).addClass("disables-context-loader"))
}
Daniel Lubarov
  • 7,796
  • 1
  • 37
  • 56
  • What browser and platform are you testing in? When I try your fiddle with Firefox in Windows, the event is fired regardless of whether I am holding control down or not. You can examine `event.ctrlKey` to check the state of the control key. – Chris Baker Nov 20 '13 at 04:35
  • I'm using Chrome v31 on OSX 10.8.5. But I think we're seeing the same behavior. What I want is to see the alert *only* for a normal left click, and for a new tab to open if control/meta is held *or* the context menu is used. – Daniel Lubarov Nov 20 '13 at 04:36
  • If your links are links, users can make their own decision where to open them - same window, new window or new tab. Why do you need to detect anything? What problem are you trying to solve? – RobG Nov 20 '13 at 04:52
  • possible duplicate question http://stackoverflow.com/questions/14954487/how-do-identify-whether-the-window-opened-is-a-pop-up-or-a-tab – malcolmX Nov 20 '13 at 04:57
  • @RobG, exactly - I'm trying to preserve the user's intent when it comes to deciding how to open links. What makes the situation unique is that if a link would be opened in the current window, I want to prevent the extra HTTP request and do some DOM manipulation instead. – Daniel Lubarov Nov 20 '13 at 05:00
  • 2
    I disagree with the duplicate marking, and definitely that the answer there applies here. If you need to detect, under no uncertain terms and with no possibility of failure, a new tab (like for an extension) then sure, jump through the hoops. For a website, I think this problem can be handled in the way I've outlined in my answer. There's no need to over-complicate this -- handle the main use cases and avoid the edge-case crazy stuff, because it is more likely to become outdated or have some unintended effect that pisses off your user. – Chris Baker Nov 20 '13 at 05:01

2 Answers2

45

You can examine the ctrlKey, shiftKey, and metaKey properties of the event object. If either is true, the key control, shift, or meta (Apple's command) key is being held and you should allow the default link action to proceed. Otherwise, you use preventDefault to stop the link action and handle it with javascript instead.

Add target="_blank" to your anchor markup, so the default link behavior is opening a new tab. Otherwise it will open on top of the current page (that may be desired).

Here's the javascript, either way:

document.getElementById("mylink").onclick = function(evnt) {
    if (
        evnt.ctrlKey || 
        evnt.shiftKey || 
        evnt.metaKey || // apple
        (evnt.button && evnt.button == 1) // middle click, >IE9 + everyone else
    ){
        return;
    }
    evnt.preventDefault();

    alert("clicked");
    return false;
}

Fiddle: http://jsfiddle.net/6byrt0wu/

Documentation

Chris Baker
  • 49,926
  • 12
  • 96
  • 115
  • 1
    But the keys are somewhat OS- and browser-specific. I could try to determine which modifier keys cause new tabs/windows based on `window.navigator`, I was just wondering if there was a better approach. – Daniel Lubarov Nov 20 '13 at 04:50
  • 2
    What about where a link is opened in a new tab or window without any click event? @Daniel—browser sniffing is an awful solution, unless you are being paid to maintain your code when it breaks when new platforms appear. – RobG Nov 20 '13 at 04:54
  • 1
    @Daniel The only OS-specific key I can think of is the Apple command key. I've modified the code to include that as well. As far as the edge cases, I don't believe you need to cover them. If the user is doing something other than holding control, shift, or command when they click the link, the code will simply act like it would had they normal-clicked it. Browser-sniffing is over-engineering the solution. – Chris Baker Nov 20 '13 at 04:56
  • @RobG As in keyboard navigation? That's not covered in this question, but the approach would be the same except you'd use the appropriate keydown/keyup event. – Chris Baker Nov 20 '13 at 04:58
  • @Chris–the Apple touch interface allows opening a link in a new tab or window without dispatching a click event. It also allows copying and pasting links without causing a click event. – RobG Nov 20 '13 at 12:02
  • @RobG With a long-press, yes? OP said he wanted to leave the option open to the user. If long-press doesn't fire the click event at all, then that works out well for this use case. – Chris Baker Nov 20 '13 at 13:39
  • 2
    @chris—with a two–finger tap, or a drag or multiple click to select, then copy using keyboard or context menu. There is also right–click + "open" on the context menu, which opens the link in the current window and doesn't dispatch a click event, plus various plugins and accelerators. I think the key point is that any method will be unreliable and only work some of the time (and the user likely wont know which method is being used when). Also, keeping the result of a server page identical to a local page seems an unnecessary inconvenience for site maintainers (but that's the OPs problem). – RobG Nov 20 '13 at 22:47
  • @RobG All of those considerations are valid, buuut, it is my impression that the OP wants to capture standard clicks and act on those. Any non-standard click, such as with a modifier key or any of the ones you mention, should cause the link to behave in a default way, rather than triggering the javascript. So again, while all of that stuff is true, it doesn't seem like a deal breaker, or even anything the OP has to worry about at all. This code will allow him to capture a standard click, and everything else we leave to the user agent and the user. – Chris Baker Nov 20 '13 at 23:15
  • This even works on my mac where I have enabled 3-finger taps to open in new tab. Awesome! – jetlej Nov 21 '14 at 21:23
  • 1
    Doesn't work for my most used open in new tab method; the 3rd button (mousewheel) click. – Joel Peltonen Jan 23 '15 at 08:53
  • 1
    @Nenotlep Not such a tough one to solve; I edited the answer to add that. – Chris Baker Jan 23 '15 at 14:49
  • 3
    @ChrisBaker Cool :) The middle click solution doesn't work for Firefox 35 on Linux, but it does work for Chromium 39. Also it might be a good option to disallow rightclicking because the open in new tab from there is still accessible. – Joel Peltonen Jan 26 '15 at 06:52
0

You can detect that using onblur as well

<html>
<head>
<script>
function newTabaction() {
  document.getElementById("demo").innerHTML = "New tab opened!<br><br>refesh this page to recheck ";
}
window.onblur = newTabaction;
</script>
</head>
<body>
<div id="demo">
Open a new tab and then check this page
</div>
</body>
</html>
Godwin
  • 400
  • 3
  • 15