How to navigate to an href from within a Javascript function - javascript

I have a webapp problem.
double-touching specific button in an iOS device (iPad)
triggers a click event which triggers some iOS default behaviour, which scales the page.
I want to prevent this from happening.
So I added an eventListener to the click event, at a top-element elementId1.
elementId1
elementId2
elementId3
elementId4
I choose the top-element elementId1 because this side effect happens on various nested elements, and I want to have a single catch-all click eventListener, at the top of the html element hierarchy, that will prevent this from happening in multiple places.
The eventListener triggers a function onClickTopElement that calls event.preventDefault()
The eventListener is set with attributes:
capture: false - this cause the event to trigger in the bubbling phase, i.e. on the way up and not on the way down
This means that:
the event first fires on the element which may have a function to trigger on click and do what it was designed to do.
as the event bubbles up, it gets to the elementId1 element, which fires the function onClickTopElement which calls event.preventDefault(), which disables the iOS default behaviour of scaling up the page.
// add catch-all 'click' eventListener, at the top of the html elements so that on iOS the side-effect of scaling-the-page when double-touching never happens
let element1 = document.getElementById('elementId1');
element1.addEventListener( 'click', onClickTopElement, {capture: false, passive: false} );
function onClickTopElement(event) {
// prevent from trickling the event, when touching, which causes, in iOS to zoom the entire page
event.preventDefault();
}
The end result is that double-touching the buttons does not cause the page to scale up. Good!
This is good but creates another side-effect.
There is a button (elementId4) with href tag that when clicked, takes to another page.
This is done by the browser's default behaviour.
But since elementId4 is nested within elementId1, the default onClick behaviour is now prevented.
One solution is to add specific onClick eventListener to elementId4 and do what the default behaviour does, i.e. navigate to the href.
What would be the command to navigate to the href within an onClick eventListener function.
Thanks

Related

preventDefault() is insufficient in anchor event delegation in iframe?

I hope this might be a low-hanging fruit for all you web-dev experts out there:
I have implemented event delegation by attaching a mouse click handler to a parent element of an unordered list of anchor elements (classic navigation design). The document I am working on exists in an iFrame. The handler looks like this:
const handleMouseClick = function(evt) {
if (evt.target.matches('a')) {
evt.preventDefault();
evt.stopPropagation();
console.log('Clicked a tag....', event.target.href);
}
};
Just to provide a full context, I am clicking through an overlay element that takes no pointer events - so it should have no influence on click handling.
Anyway, when clicking on one of the anchor tags, I would like to prevent the iframe from loading the URL referenced in the anchor. The before mentioned click handler, which is attached to a parent, receives the click event just fine and I would have expected that calling event.preventDefault() would do the trick. However, I have to call both
event.preventDefault() and event.stopPropagation() to stop the iframe from loading the link. If I remove either one of them, the iframe loads the link?
My first thought on this is that this is unexpected, when looking at the literature on preventDefault and stopPropagation but I might be missing something. What am I missing - why is event.preventDefault() not sufficient in this case?

How to make the whole page clickable without using any cover elements with Javascript?

With Javascript, I'm looking for a way to make the whole page to redirect the user to another, no matter where they click on the page, and no matter if that element already has a click event listener. I want to redirect them no matter what.
Also, I do not want to use a cover element with fixed style and make them click on that, if possible.
Here's what I've tried so far:
function clickBody()
{
window.location.href = 'http://google.com';
}
document.body.addEventListener("click", clickBody);
It works great, except the fact that it's not redirecting if the user clicks on the anchor tags.
Is there any solution to make this happen?
Add true as the last arg to addEventListener (it means that it's capture event and should be handled before regular events) and stop event propagation and prevent default in your handler to prevent redirect on links click:
function clickBody(e) {
e.preventDefault();
e.stopPropagation();
window.location.href = 'http://google.com';
}
document.body.addEventListener("click", clickBody, true);
You will want to use the capturing phase of the event lifecycle. From MDN
In the capturing phase:
The browser checks to see if the element's outer-most ancestor
() has an onclick event handler registered on it in the
capturing phase, and runs it if so. Then it moves on to the next
element inside and does the same thing, then the next one, and
so on until it reaches the element that was actually clicked on.
In the bubbling phase, the exact opposite occurs:
The browser checks to see if the element that was actually clicked on
has an onclick event handler registered on it in the bubbling phase,
and runs it if so. Then it moves on to the next immediate ancestor
element and does the same thing, then the next one, and so on until it
reaches the element.
function clickBody()
{
window.location.href = 'http://google.com';
}
document.body.addEventListener("click", clickBody, true);
The third parameter here designates the event as capturing. https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener
In all likelihood, the javascript should execute fast enough to beat out the default action for the link. But you may want to add event.preventDefault() just to be on the safe side.

Is it possible to force event listeners to fire in a particular order in JavaScript?

Is there a way to be notified or perform some callback function once an event has finished propagating in JavaScript?
Equivalently, and more specifically: is there a way to 'prioritize' an event and make sure that it is called after every other event listener has been fired (similarly almost to the !important value in CSS).
For instance, if I have 3 event listeners - 2 attached to the window and 1 to some button element. Can I force a certain one of those events to be called LAST, regardless of where it lies in the DOM? I understand that there are event phases and the ability to attach a listener to the capture or bubbling phase but this still means there's a preset order.
edit: the specific problem:
I'm attempting to build components (in React JS) which are aware of a click being registered outside of themselves (i.e. anywhere on the window/document except themselves) - often as a way of closing/hiding the component. Each of these components will register a listener on the window object which fires a function belonging to that component.
The trouble is, when another component [B] (inherently lower down in the DOM than the window) is clicked to let's say toggle the display of [A], [B]'s event fires first and toggles the state 'showA', the event bubbles up and [A]'s window event listener kicks in and re-toggles the state 'showA' - so, [A] remains hidden after changing state twice. I can't use stopPropagation as other window events need to fire. I've tried to unbind listeners but this doesn't happen in time.
An example of what currently happens all in one go is:
'show thing' button clicked
add listener to window for closing 'thing'
'window but not thing' was clicked
remove listener to close 'thing'
If only I could wait until the click event had finished bubbling before adding the new listener, I'd have no issue
I did leave an answer to your original question but I see you've updated it. I wouldn't say this is React specific but a common implementation for components that need to close/de-activate when the document is clicked.
For instance, the following snippet is an implementation for a speed dial spin out button;
(function () {
var VISIBLE_CLASS = 'is-showing-options',
btn = document.getElementById('.btn'),
ctn = document.getElementById('.ctn'),
showOpts = function(e) {
var processClick = function (evt) {
if (e !== evt) {
ctn.classList.remove(VISIBLE_CLASS);
ctn.IS_SHOWING = false;
document.removeEventListener('click', processClick);
}
};
if (!ctn.IS_SHOWING) {
ctn.IS_SHOWING = true;
ctn.classList.add(VISIBLE_CLASS);
document.addEventListener('click', processClick);
}
};
btn.addEventListener('click', showOpts);
}.call(this));
When the user clicks the button, the container is shown for the speed dial options and an event listener is bound to the document. However, you need to make sure that the initial event that is fired is not the one that triggers the takedown straight away (this is sometime a gotcha). This check is made with if (e !== evt) .... For further clicks the event check is made and the relevant action taken ending in removal of the event listener from the document.
Of course in your particular case if you want to only close when the element isn't clicked then you could make relevant checks on the evt.target and evt.currentTarget in the callback (in the snippet case, processClick).
Hopefully, this can help you out with registering close down callbacks for your individual components.

Blur event stops click event from working?

It appears that the Blur event stops the click event handler from working? I have a combo box where the options only appear when the text field has focus. Choosing an option link should cause an event to occur.
I have a fiddle example here: http://jsfiddle.net/uXq5p/6/
To reproduce:
Select the text box
Links appear
Click a link
The blur even occurs and the links disappear
Nothing else happens.
Expected behavior:
On step 5, after blur occurs, the click even should also then fire. How do I make that happen?
UPDATE:
After playing with this for a while, it seems that someone has gone to great lengths to prevent an already-occurred click event from being handled if a blur event makes the clicked element Un-clickable.
For example:
$('#ShippingGroupListWrapper').css('left','-20px');
works just fine, but
$('#ShippingGroupListWrapper').css('left','-2000px');
prevents the click event.
This appears to be a bug in Firefox, since making an element un-clickable should prevent future clicks, but not cancel ones that have already occurred when it could be clicked.
Other things that prevent the click event from processing:
$('#ShippingGroupListWrapper').css('z-index','-20');
$('#ShippingGroupListWrapper').css('display','none');
$('#ShippingGroupListWrapper').css('visibility','hidden');
$('#ShippingGroupListWrapper').css('opacity','.5');
I've found a few other questions on this site that are having similar problems. There seem to be two solutions floating around:
Use a delay. This is bad because it creates a race condition between the hiding and the click event handler. Its also sloppy.
Use the mousedown event. But this isn't a great solution either since click is the correct event for a link. The behavior of mousedown is counter-intuitive from a UX perspective, particularly since you can't cancel the click by moving the mouse off the element before releasing the button.
I can think of a few more.
3.Use mouseover and mouseout on the link to enable/disable the blur event for the field. This doesn't work with keyboard tabing since the mouse is not involved.
4.The best solution would be something like:
$('#ShippingGroup').blur(function()
{
if($(document.activeElement) == $('.ShippingGroupLinkList'))
return; // The element that now has focus is a link, do nothing
$('#ShippingGroupListWrapper').css('display','none'); // hide it.
}
Unfortunately, $(document.activeElement) seems to always return the body element, not the one that was clicked. But maybe if there was a reliable way to know either 1. which element now has focus or two, which element caused the blur (not which element is blurring) from within the blur handler. Also, is there any other event (besides mousedown) that fires before blur?
click event triggers after the blur so the link gets hidden. Instead of click use mousedown it will work.
$('.ShippingGroupLinkList').live("mousedown", function(e) {
alert('You wont see me if your cursor was in the text box');
});
Other alternative is to have some delay before you hide the links on blur event. Its upto you which approach to go for.
Demo
You could try the mousedown event instead of click.
$('.ShippingGroupLinkList').live("mousedown", function(e) {
alert('You wont see me if your cursor was in the text box');
});
This is clearly not the best solution as a mousedown event is not achieved the same way for the user than a click event. Unfortunately, the blur event will cancel out mouseup events as well.
Performing an action that should happen on a click on a mousedown is bad UX. Instead, what's a click effectively made up of? A mousedown and a mouseup.
Therefore, stop the propagation of the mousedown event in the mousedown handler, and perform the action in the mouseup handler.
An example in ReactJS:
<a onMouseDown={e => e.preventDefault()}
onMouseUp={() => alert("CLICK")}>
Click me!
</a>
4.The best solution would be something like:
$('#ShippingGroup').blur(function()
{
if($(document.activeElement) == $('.ShippingGroupLinkList'))
return; // The element that now has focus is a link, do nothing
$('#ShippingGroupListWrapper').css('display','none'); // hide it.
}
Unfortunately, $(document.activeElement) seems to always return the
body element, not the one that was clicked. But maybe if there was a
reliable way to know either 1. which element now has focus or two,
which element caused the blur (not which element is blurring) from
within the blur handler.
What you may be looking for is e.relatedTarget. So when clicking the link, e.relatedTarget should get populated with the link element, so in your blur handler, you can choose not to hide the container if the element clicked is within the container (or compare it directly with the link):
$('#ShippingGroup').blur(function(e)
{
if(!e.relatedTarget || !e.currentTarget.contains(e.relatedTarget)) {
// Alt: (!e.relatedTarget || $(e.relatedTarget) == $('.ShippingGroupLinkList'))
$('#ShippingGroupListWrapper').css('display','none'); // hide it.
}
}
(relatedTarget may not be supported in older browsers for blur events, but it appears to work in latest Chrome, Firefox, and Safari)
If this.menuTarget.classList.add("hidden") is the blur behavior that hides the clickable menu, then I succeeded by waiting 100ms before invoking it.
setTimeout(() => {
this.menuTarget.classList.add()
}, 100)
This allowed the click event to be processed upon the menuTarget DOM before it was hidden.
I know this is a later reply, but I had this same issue, and a lot of these solutions didn't really work in my scenario. mousedown is not functional with forms, it can cause the enter key functionality to change on the submit button. Instead, you can set a variable _mouseclick true in the mousedown, check it in the blur, and preventDefault() if it's true. Then, in the mouseup set the variable false. I did not see issues with this, unless someone can think of any.
I have faced a similar issue while using jQuery blur, click handlers where I had an input name field and a Save button. Used blur event to populate name into a title placeholder. But when we click save immediately after typing the name, only the blur event gets fired and the save btn click event is disregarded.
The hack I used was to tap into the event object we get from blur event and check for event.relatedTarget.
PFB the code that worked for me:
$("#inputName").blur(function (event) {
title = event.target.value;
//since blur stops an immediate click event from firing - Firing click event here
if (event.relatedTarget ? event.relatedTarget.id == "btnSave" : false) {
saveBtn();
}
});
$("#btnSave").click(SaveBtn)
As already discussed in this thread - this is due to blur event blocking click event when fired simultaneously. So I have a click event registered for Save Btn calling a function which is also called when blur event's related Target is the Save button to compensate for the click event not firing.
Note: Didnt notice this issue while using native onclick and onblur handlers - tested in html.

prevent browser from generating double click event

I have two overlapping div elements in my web app.
On the underlying div an event is registered on double click.
On the div that lies above the other div a event is registered on click that hides the div.
The problem is that if I double click on the above div element it is hidden after the first click BUT the second click causes the double click event on the underlying div to fire - how can I prevent that?
Mixing "click" and "double-click" is going to be problematic at best. However, in this case things might get better if you just ensure that your handler for the "click" event (the event that hides the element) returns false to the browser.
How to do that depends on how your handler is registered. If it's like this:
<div onclick='hideMe();'>
then you'd change that to
<div onclick='hideMe(); return false'>
If you're using some framework or some other means of attaching the handler, then just having the handler function return false should do it.

Categories