Question
I want to trigger a (middle) mouse click on a link, in a way that triggers the native browser behavior for this event.
E.g. in those browsers I work with, middle-click will cause the linked page to open in a new tab in the background.
But if other browsers have a different behavior, that should be triggered instead.
I know there is the element.click(); method, but how do I tell it which mouse button should be clicked?
Background
The background is that I want to make a div behave as much as possible like a regular link. The idea was that I would create a hidden link tag and trigger the native browser behavior.
Requirements for the div-as-a-link:
href can come from a data-href attribute.
Native browser behavior for left-click, middle-click and possibly right-click.
respect of the target attribute, which could come from a data-target attribute.
tabindex
activation with the keyboard?
possibility to select text snippets within the div. This means we cannot just use an overlay.
Why not use a native link tag? Because the div contains inner <a> tags. So making the div box an a-tag would cause nested a-tags which would cause some browsers to restructure the DOM tree.
Obviously I could just set document.location.href on click. But this is only 20% of the browser's native behavior.
I could also try to detect the mouse button, and use js to open the tab in the background, if it was the middle button.
But maybe some browsers have a different behavior for middle-click. I would rather let the browser do its thing, than trying to replicate a specific browser behavior with js.
The idea I had was to create a hidden <a> tag with the same href, and delegate click events from the <div> to this hidden <a> tag. For this, I need to trigger the event with the same mouse button. I am not sure how to do this.
js or jQuery?
jQuery is ok for my personal use case. But to make this useful to a wider audience, maybe also post how to do it without jQuery, if you know.
See also
There are some question which deal with this kind of problem, but each of them looks at a different angle or has different constraints.
This is what I come up with thanks to #JonUleis in the comments and #MikeWillis in https://stackoverflow.com/a/32868971/246724
$('.linkbox', context).once('linkbox').each(function(e){
var $linkbox = $(this);
var href = $linkbox.data('href');
if (!href) {
return;
}
var $hiddenLink = $('<a>').attr('href', href).hide().appendTo('body');
$linkbox.click(function(e){
$hiddenLink[0].dispatchEvent(new MouseEvent('click', {button: e.button, which: e.which}));
e.preventDefault();
});
});
Note that I put the hidden link outside of the clicked div, to prevent recursion.
The right-click menu does not give me "copy link url", but I imagine this would be really hard to replicate.
UPDATE: I found that I don't really need to append the link to anything, and it still works. But I only tested this in Chromium on Linux so far.
This is the code to achieve left click and middle click functionality.
tabindex works with any html element so you can use it not the div
$("#link").on('click', function(e) {
const hasTargetBlank = $(this).data('target') === '_blank';
const href = $(this).data('href');
switch(e.which) {
// left click
case 1: {
if (hasTargetBlank) {
window.open(href);
} else {
window.location = href;
}
break;
}
// middle click
case 2: {
window.open(href);
break;
}
// right click
case 3: {
// do what you want to do
}
}
});
Related
The Problem
For site testing, I am attempting to emulate a middle click (or scroll wheel click) on an element like the one below, using JS that is based on some other answers on here.
HTML:
<a
id="example-link" href="https://www.google.com/"
target="_blank"
onclick="window.open('https://www.google.com/', '',
'width=500, height=500, resizable=1, scrollbars=1');
return false;">
<span>Middle Click Me</span>
</a>
JS (looking to change something here):
var middleClick = new MouseEvent( "click", { "button": 1, "which": 2 });
jQuery("#example-link")[0].dispatchEvent( middleClick );
The issue is shown in this jsfiddle.
Note that a regular click should normally dispatch a popup. jsfiddle is catching the popup and opening it in a new tab.
jsFiddle is used instead of a stackoverflow fiddle to avoid confusion, as the stack overflow fiddle doesn't properly handle window.open().
Compare 1) middle-clicking manually to 2) running the code to emulate a click.
The problem is, when I trigger the middle click programmatically, it triggers both the onclick method of opening a new window, as well as opening the link in a new tab (creating both a popup and a tab instead of just a tab). If this link is middle clicked manually, the popup will not trigger.
The Question
Is there a better way of emulating middle clicks (preferably cross-browser compatible)? The way it is written, I can't really get the MouseEvent script to accurately emulate manual middle clicks.
Edit: unfortunately, I can't change the HTML object or modify the DOM for the purpose of testing integrity, so I would prefer that the onclick tag stays on the element.
To the best of my understanding, as long as that inline click handler is there, it will fire on every click event on that element, but here are two workarounds I've come up with.
If you really can't modify the DOM even for an instant, then the first option won't serve. It cheats by changing the DOM and then restoring it, hopefully before anyone notices.
The second option avoids the need to click the link at all by reading its attributes and using them to open a new tab, without firing a new click event. (This relies on the script having a basic understanding of link element's markup. Specifically, the code I've written here simply assumes that the "link" element has a valid url in its href attribute -- but if necessary, you could make other, more complex assumptions, and act conditionally based on the properties that the "link" element turns out to have at runtime.)
Note: A third alternative would be create a custom event that has all the same core behavior as the built-in click event but without actually being a click event, the idea being to fool the onclick handler into sleeping through the event. I'm not sure how much work it would take to do a sufficiently good job of rebuilding this particular wheel. Maybe it wouldn't be too bad?
In this script, the functions that open the new tab are triggered by button clicks, but of course this is just for demo purposes, and you'd presumably trigger them on page load or as callbacks to some other action. And the anchor element in the snippet has been changed slightly, too, for space-saving reasons
.
(Because the direct call to window.open doesn't appear to work properly in the Stack Overflow snippet, I followed your lead and pasted the code into a fiddle so it can be seen running in all its nominal glory.)
const
// Identifies some DOM elements
cheatButton = document.getElementById("cheat-button"),
mimicButton = document.getElementById("mimic-button"),
exampleLink = document.getElementById("example-link"),
// Defines an event to be dispatched programmatically
middleClick = new MouseEvent("click", {button: 1, which: 2});
// Demonstrates calling the functions
cheatButton.addEventListener("click", emulateMiddleClick); // Option 1
mimicButton.addEventListener("click", followLink); // Option 2
// Defines the functions
function emulateMiddleClick(){
const onclickHandler = exampleLink.onclick; // Remembers onclick handler
exampleLink.onclick = ""; // Supresses onclick handler
exampleLink.dispatchEvent(middleClick); // Dispatches middle click
exampleLink.onclick = onclickHandler; // Restores onclick handler
}
function followLink(){
if(!exampleLink.href){ return; } // In case href attribute is not set
const {href, target} = exampleLink; // Gets props by using "destructuring"
// This doesn't seem to work in the Stack Overflow snippet...
window.open(href, (target || "_blank")); // In case target attribute is not set
// ...But we can at least log our intention to open a new tab
console.log(`opening: "${href}" (in "${target}")`);
}
div{ margin: 1em; }
button{ padding: 0.3em; border-radius: 0.5em; }
span{ margin: 1.3em; }
<div>
<button id="cheat-button">Fake Click, and Suppress Handler</button>
</div>
<div>
<button id="mimic-button">Follow Link without Clicking</button>
</div>
<a id="example-link"
href="https://www.google.com/"
target="_blank"
onclick="window.open('https://www.google.com/', '', 'width=50, height=50');">
<span>Middle Click Me</span>
</a>
When picture <img> is inside <a> tag,
is there a way that i can still use middle click drag on like on any other ellemenet to scroll/navigate page instead of default behavior where middle click open new tab or does not scroll on drag?
I do understand that i can maybe use different tag on element and go with click event on it, but i would not wish to do it like that if possible.
You could use the which property. But this property is browser specific.
$("a").mousedown(function(event){
if(event.which == 2){
event.preventDefault()
}
}
How to know when click into iframe in html? because i want to close the dropdown component when click into iframe.
I searched by google, there is a solution using window.blur, but this method isn't standard.
Any helps are appreciate, thanks!
You may be able to do this with Javascript. The following question covers some methods on how you might implement an OnClick() method for iframes:
Adding click event handler to iframe
From there, you could probably have the function go ahead and close the dropdown.
Without knowing more about your code, we can only really suggest general solutions.
I don't think there is a direct way to get a click on iframe in javascript. But there is way around for it.
What we can instead do is track if the user hovers over the iframe and using $(window).blur() we can know if the focus has shifted from current window i.e. the webpage to iframe embedded. Focus shift will mean that user has clicked inside the iframe. Once we capture $(window).blur(), we can toggle dropdown state.
var iframeHover;
$('iframe').hover(function() {
iframeHover = true;
}, function() {
iframeHover = false;
});
$(window).blur(function() {
if (iframeHover)
$("#dLabel").dropdown('toggle');
});
Working Plnkr is: Plnkr
I have a HTML document containing a series of Questions and Answers.
Each Question is contained in a div. Each Answer is contained in a div. Each Answer is given a unique id.
The CSS style for each Answer is set to 'none' in order to initially hide the element.
When a Question is clicked on, the div passes the id of the corresponding Answer to this function:
function toggle(id) {
var state = document.getElementById(id).style.display;
if (state == 'block') {
document.getElementById(id).style.display = 'none';
} else {
document.getElementById(id).style.display = 'block';
}
}
(This toggles the appearance of the Answer div.)
The problem: This mechanism works well in Safari if the scrollbar is at the default (top) position. Otherwise, this will cause the scrollbar to reset to the default position, effectively ruining the usefulness of this mechanism on long pages.
Q. Is there any way to prevent the scrollbar position from being reset?
Thanks.
You've said that you're using
<a href='#'>...</a>
...for the question. That's the problem, that's a link telling the browser to go to the top of the page. You have a couple of options for correcting it:
Hook the click event on the link (or its parent, etc.) and cancel the default action. How you do that depends on how you hook the event. It sounds like you're already hooking the event, so this is probably the simplest solution. If you're using an onclick= style hook, return false; out of your handler function. If you're using addEventListener, use preventDefault on the event passed into it. (You will presumably already be aware that most versions of IE don't support addEventListener, but rather attachEvent; how you prevent the default is also different. This sort of thing is why I use a library like jQuery, Prototype, YUI, Closure, or any of several others to smooth over browser differences.)
Use the javascript: pseudo-protocol instead of #, e.g.:
<a href='javascript:;'>...</a>
The pseudo-protocol tells the browser to run the script code in the href element. In the above, the code is just a ; (statement terminator), which does nothing. You could, of course, have it trigger your toggle function:
<a href='javascript:toggle("foo");'>...</a>
Don't use an a at all. Whether it's appropriate to use one depends on whether it really is conceptually a link, which is totally your call.
I find myself very often in the situation that I open an element in a web page - e.g. a drop-down menu - that I want to close if the user clicks anywhere on the page except the element itself.
To keep things simple, I have mostly written the code myself instead of employing some drop-down menu class.
However, I have never managed to build an implementation of this that was completely satisfying: Event handling and bubbling would work differently in different browsers, there would be the need for nasty workarounds, in some situations clicking the drop-down button would start closing it in the same moment, and so on.
Is there a Prototype based, authoritative, best practice to do this? Something that works across browsers - IE6 being a plus but not a requirement?
Just this:
click on a button - an element opens
(e.g. an absolutely positioned drop-down menu).
click within the element - the element stays open.
click on the button that opened the element - the element stays open.
click anywhere else on the page - the element closes.
I need help with the event handling part only, the displaying of the menu is totally secondary.
Event.observe(document, 'click', function (event) {
switch (event.element().id) {
case 'example_id':
// do different stuff depending on element clicked
// ofc u don't need to pass id, u can simply throw an element itself
break;
default:
// do close action
break;
}
// also check Event.findElement();
});
You can also add specific classes to the items you don't want to trigger close action and check it inside
if (!event.element().hasClassName('dont_close'))
Element.remove(selectDOMElement);
I guess the open button is within the menu.
$('openbutton').observe('click' function(event) {
var menu = $('openbutton').up();
if (menu.hasClassName('collapsed')) {
menu.removeClassName('collapsed');
menu.addClassName('expanded');
document.observe('click', function (event) {
if(!event.target.descendantOf(menu)) {
menu.addClassName('collapsed');
menu.removeClassName('expanded');
}
});
} else {
menu.addClassName('collapsed');
menu.removeClassName('expanded');
}
});
AFAIK, you need to make an invisible div the size of window, put it behind the current element, and add a click event to that.
Just thinking out loud but you might be able to use the blur event on the dropdown to hide it (blur gets fired when an element loses focus) or another idea might be when the dropdown opens attach a click event to the document object that hides the dropdown. Events get propagated through their containers so it should end up at the document object eventually. You might need to call preventPropegation on the event when your dropdown gets clicked so that it doesn't make it to the handler attached to the document.
maybe you could calculate the Position (X,Y) for the clickevent and compare that to the cumulativeOffset (cumulativeScrollOffset) + [el.width|el.height] of the desired container.
Event.observe(window, 'click', function(e) {
var el = $('el')
if( el.cumulativeOffset[0] < e.Event.pointerX(e) ... )
});
<div id="el" style="position:absolute;width:100px;height:100px;background-color:#00F;top:100px;left:300px;">
</div>