I have a handler attached to an event and I would like it to execute only if it is triggered by a human, and not by a trigger() method. How do I tell the difference?
For example,
$('.checkbox').change(function(e){
if (e.isHuman())
{
alert ('human');
}
});
$('.checkbox').trigger('change'); //doesn't alert
You can check e.originalEvent: if it's defined the click is human:
Look at the fiddle http://jsfiddle.net/Uf8Wv/
$('.checkbox').change(function(e){
if (e.originalEvent !== undefined)
{
alert ('human');
}
});
my example in the fiddle:
<input type='checkbox' id='try' >try
<button id='click'>Click</button>
$("#try").click(function(event) {
if (event.originalEvent === undefined) {
alert('not human')
} else {
alert(' human');
}
});
$('#click').click(function(event) {
$("#try").click();
});
More straight forward than above would be:
$('.checkbox').change(function(e){
if (e.isTrigger)
{
alert ('not a human');
}
});
$('.checkbox').trigger('change'); //doesn't alert
Currently most of browsers support event.isTrusted:
if (e.isTrusted) {
/* The event is trusted: event was generated by a user action */
} else {
/* The event is not trusted */
}
From docs:
The isTrusted read-only property of the Event interface is a Boolean
that is true when the event was generated by a user action, and false
when the event was created or modified by a script or dispatched via
EventTarget.dispatchEvent().
I think that the only way to do this would be to pass in an additional parameter on the trigger call as per the documentation.
$('.checkbox').change(function(e, isTriggered){
if (!isTriggered)
{
alert ('human');
}
});
$('.checkbox').trigger('change', [true]); //doesn't alert
Example: http://jsfiddle.net/wG2KY/
Accepted answer didn't work for me. It's been 6 years and jQuery has changed a lot since then.
For example event.originalEvent returns always true with jQuery 1.9.x. I mean object always exists but content is different.
Those who use newer versions of jQuery can try this one. Works on Chrome, Edge, IE, Opera, FF
if ((event.originalEvent.isTrusted === true && event.originalEvent.isPrimary === undefined) || event.originalEvent.isPrimary === true) {
//Hey hooman it is you
}
Incase you have control of all your code, no alien calls $(input).focus() than setFocus().
Use a global variable is a correct way for me.
var globalIsHuman = true;
$('input').on('focus', function (){
if(globalIsHuman){
console.log('hello human, come and give me a hug');
}else{
console.log('alien, get away, i hate you..');
}
globalIsHuman = true;
});
// alien set focus
function setFocus(){
globalIsHuman = false;
$('input').focus();
}
// human use mouse, finger, foot... whatever to touch the input
If some alien still want to call $(input).focus() from another planet.
Good luck or check other answers
I needed to know if calls to the oninput handler came from the user or from undo/redo since undo/redo leads to input events when the input's value is restored.
valueInput.oninput = (e) => {
const value = +valueInput.value
update(value)
if (!e.inputType.startsWith("history")) {
console.log('came from human')
save(value)
}
else {
console.log('came from history stacks')
}
}
It turns out that e.inputType is "historyUndo" on undo and "historyRedo" on redo (see list of possible inputTypes).
You can use onmousedown to detect mouse click vs trigger() call.
I would think about a possibility where you check the mouse position, like:
Click
Get mouse position
Overlaps the coords of the button
...
Related
In JavaScript, is it possible to distinguish between beforeunload events that were triggered by the user closing a browser tab vs clicking a mailto link?
Basically, I would like to do this:
window.addEventListener("beforeunload", function (e) {
if(browserTabClosed) {
// Do one thing
}
else if (mailtoLinkClicked) {
// Do a different thing
}
}
Found a solution by looking at the event (e below) that gets passed in:
window.addEventListener("beforeunload", function (e) {
// We can use `e.target.activeElement.nodeName`
// to check what triggered the passed-in event.
// - If triggered by closing a browser tab: The value is "BODY"
// - If triggered by clicking a link: The value is "A"
const isLinkClicked = (e.target.activeElement.nodeName === "A");
// If triggered by clicking a link
if (isLinkClicked) {
// Do one thing
}
// If triggered by closing the browser tab
else {
// Do a different thing
}
}
The beforeunload method has an unstable behaviour between browsers, the reason is that browser implementations try to avoid popups and other malicious code runned inside this handler.
There is actually no general (cross-browser) way to detect what triggered the beforeunload event.
Said that, in your case you could just detect a click on the window to discriminate between the two required behaviours:
window.__exit_with_link = false;
window.addEventListener('click', function (e) {
// user clicked a link
var isLink = e.target.tagName.toLowerCase() === 'a';
// check if the link has this page as target:
// if is targeting a popup/iframe/blank page
// the beforeunload on this page
// would not be triggered anyway
var isSelf = !a.target.target || a.target.target.toLowerCase() === '_self';
if (isLink && isSelf) {
window.__exit_with_link = true;
// ensure reset after a little time
setTimeout(function(){ window.__exit_with_link = false; }, 50);
}
else { window.__exit_with_link = false; }
});
window.addEventListener('beforeunload', function (e) {
if (window.__exit_with_link) {
// the user exited the page by clicking a link
}
else {
// the user exited the page for any other reason
}
}
Obviously it is not the proper way, but still working.
At the same way, you could add other handlers to check other reasons the user left the page (eg. keyboard CTRL-R for refresh, etc.)
I have the following input field:
<input onPaste={pasteFunction} keyUp={keyupFunction} />
I want either the pasteFunction or keyupFunction to run, NOT both. But when a user pastes text into this field both events get triggered and both run.
Is there a way to prevent keyUp if something was pasted into the field?
I tried to set a flag and reset it:
function pasteFunction() {
pasteInProgress = true;
//etc...
pasteInProgress = false;
}
function keyupFunction() {
if (pasteInProgress) return;
//etc...
}
But this doesn't work as pasteInProgress is set to false before keyupFunction is triggered.
You could try storing the time instead, for example.
function pasteFunction() {
lastPaste = Date.now();
//etc...
}
function keyupFunction() {
//less than one second has passed.
if (Date.now()-lastPaste<1000) return;
//etc...
}
Maybe try experimenting with different times depending on the application, but it is still a "hacky" way to solve it. I think there could be better ways.
Is there a way to prevent keyUp if something was pasted into the field?
Assuming you are using a ui library (react?), you could store a value in the component state when something was pasted into the field.
this.state = {
hasPasted: false;
}
function pasteFunction() {
this.state.hasPasted = true; // could also be a timestamp if you need granularity
}
function keyupFunction(e) { // assuming you can pass event here
if (this.state.hasPasted){
// you may want to preventDefault() here
e.preventDefault();
return;
};
}
Without you sharing more context/code, this should do it.
I have a table cell as the following :
<td id=report onMouseUp='clickReport()' onKeyPress='clickReport()' >Click</td>";
The event function is as below :
function clickReport() {
document.form.submit();
}
On form submission, there is a back-end process going on. Until the 1st process completes(i.e., until the page reloads), I do not want the user to press the press the "Click" again, else it may affect the previous running process.
So, I thought of disabling the "Click" after the first press.
I tried using preventDefault() but it is not working.
function clickReport() {
document.form.submit();
document.getElementById("report").onMouseUp = function(e)
{
e.preventDefault();
return false;
}
document.getElementById("report").onKeyPress = function(e)
{
e.preventDefault();
return false;
}
}
Can someone please help!
1) You might pass the element parameter to your event functions, so you can acces the DOM element easily. See below.
<td id=report onMouseUp='clickReport(this)' onKeyPress='clickReport(this)' >Click</td>";
2) On the first function run you might null the events, so they will not fire anymore. See below.
// the *element* parameter is yor <td> element here
function clickReport(element) {
document.form.submit();
element.onMouseUp = null;
element.onKeyPress= null;
}
3) You might use onclick event instead of onmouseup and get rid of onkeypress, if you only want to make it work on click.
<td id=report onclick='clickReport(this)'>Click</td>";
function clickReport(element) {
document.form.submit();
element.onclick= null;
}
Working codepen: https://codepen.io/anon/pen/MmVNMe
this should work.
since at first the disableClick is undefined the click will fire, as soon as it fires the flag will be set to true and the click will no longer be possible.
<td id=report onMouseUp='!disableClick && clickReport()'
onKeyPress='!disableClick && clickReport()' >Click</td>"
function clickReport() {
document.form.submit();
window.disableClick = true;
}
Is there a pure Javascript event (no jQuery) that is fired when I change the CSS style of my div from block to none. I was under the impression I can catch that via "onBlur" but looks like I cannot!
Please advise!
There are no DOM events triggered for visibility changes.
The best you can do is always use the same function to adjust the block's visibility instead of changing it's style each time.
Old pattern:
function doSomething() {
alert("I'm doing something!");
myBlock.style.display = "block";
}
function doSomethingElse() {
alert("I'm doing something else now!");
myBlock.style.display = "none";
}
New pattern:
function doSomething() {
alert("I'm doing something!");
toggleMyBlock(true);
}
function doSomethingElse() {
alert("I'm doing something else now!");
toggleMyBlock(false);
}
function toggleMyBlock(show) {
if (show) {
// code here for what would be your 'it became visible' event.
} else {
// code here for what would be your 'it became invisible' event.
}
myBlock.style.display = show ? "block" : "none";
}
Going forward, the new "Intersection Observer API" is what you're looking for. It currently works in latest Chrome, Firefox and Edge. See https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API for more info.
Simple code example for observing display:none switching:
// Start observing visbility of element. On change, the
// the callback is called with Boolean visibility as
// argument:
respondToVisibility(element, callback) {
var options = {
root: document.documentElement
}
var observer = new IntersectionObserver((entries, observer) => {
entries.forEach(entry => {
callback(entry.intersectionRatio > 0);
});
}, options);
observer.observe(element);
}
respondToVisibility(myElement, isVisible => {
if (isVisible) {
doSomething();
}
});
In action: https://jsfiddle.net/elmarj/u35tez5n/5/
You could use JavaScript to listen to a DOM event for a CSS3 transition (with a minimal fade time):
CSS3 transition events
I think the closest to what you're looking for would be the DOMAttrModified event. I'm not entirely sure on it's usage, but it's along the lines of:
element.addEventListener('DOMAttrModified', function (e) {
...
});
The event handler receives a MutationEvent which should provide you enough information to determine whether display has been set to block or none.
EDIT
I may have misread the question. I don't believe a CSS change will result in a DOMAttrModified event. Initially, I had read the question to mean you were setting display with a CSS value via JavaScript. My answer may not be helpful.
When a user clicks a certain link I would like to present them with a confirmation dialog. If they click "Yes" I would like to continue the original navigation. One catch: my confirmation dialog is implemented by returning a jQuery.Deferred object which is resolved only when/if the user clicks the Yes button. So basically the confirmation dialog is asynchronous.
So basically I want something like this:
$('a.my-link').click(function(e) {
e.preventDefault(); e.stopPropogation();
MyApp.confirm("Are you sure you want to navigate away?")
.done(function() {
//continue propogation of e
})
})
Of course I could set a flag and re-trigger click but that is messy as heck. Any natural way of doing this?
Below are the bits from the code that actually worked in Chrome 13, to my surprise.
function handler (evt ) {
var t = evt.target;
...
setTimeout( function() {
t.dispatchEvent( evt )
}, 1000);
return false;
}
This is not very cross-browser, and maybe will be fixed in future, because it feels like security risk, imho.
And i don't know what happens, if you cancel event propagation.
It could be risky but seems to work at the time of writing at least, we're using it in production.
This is ES6 and React, I have tested and found it working for the below browsers. One bonus is if there is an exception (had a couple during the way making this), it goes to the link like a normal <a> link, but it won't be SPA then ofc.
Desktop:
Chrome v.76.0.3809.132
Safari v.12.1.2
Firefox Quantum v.69.0.1
Edge 18
Edge 17
IE11
Mobile/Tablet:
Android v.8 Samsung Internet
Android v.8 Chrome
Android v.9 Chrome
iOs11.4 Safari
iOs12.1 Safari
.
import 'mdn-polyfills/MouseEvent'; // for IE11
import React, { Component } from 'react';
import { Link } from 'react-router-dom';
class ProductListLink extends Component {
constructor(props) {
super(props);
this.realClick = true;
this.onProductClick = this.onProductClick.bind(this);
}
onProductClick = (e) => {
const { target, nativeEvent } = e;
const clonedNativeEvent = new MouseEvent('click', nativeEvent);
if (!this.realClick) {
this.realClick = true;
return;
}
e.preventDefault();
e.stopPropagation();
// #todo what you want before the link is acted on here
this.realClick = false;
target.dispatchEvent(clonedNativeEvent);
};
render() {
<Link
onClick={(e => this.onProductClick(e))}
>
Lorem
</Link>
}
}
I solved problem by this way on one of my projects. This example works with some basic event handling like clicks etc. Handler for confirmation must be first handler bound.
// This example assumes clickFunction is first event handled.
//
// you have to preserve called function handler to ignore it
// when you continue calling.
//
// store it in object to preserve function reference
var ignoredHandler = {
fn: false
};
// function which will continues processing
var go = function(e, el){
// process href
var href = $(el).attr('href');
if (href) {
window.location = href;
}
// process events
var events = $(el).data('events');
for (prop in events) {
if (events.hasOwnProperty(prop)) {
var event = events[prop];
$.each(event, function(idx, handler){
// do not run for clickFunction
if (ignoredHandler.fn != handler.handler) {
handler.handler.call(el, e);
}
});
}
}
}
// click handler
var clickFunction = function(e){
e.preventDefault();
e.stopImmediatePropagation();
MyApp.confirm("Are you sure you want to navigate away?")
.done(go.apply(this, e));
};
// preserve ignored handler
ignoredHandler.fn = clickFunction;
$('.confirmable').click(clickFunction);
// a little bit longer but it works :)
If I am understanding the problem correctly, I think you can just update the event to be the original event in that closure you have there. So just set e = e.originalEvent in the .done function.
https://jsfiddle.net/oyetxu54/
MyApp.confirm("confirmation?")
.done(function(){ e = e.originalEvent;})
here is a fiddle with a different example (keep the console open so you can see the messages):
this worked for me in chrome and firefox
I solved this by:
placing a event listener on a parent element
removing the class from the link ONLY when the user confirms
reclicking the link after I have removed the class.
function async() {
var dfd = $.Deferred();
// simulate async
setTimeout(function () {
if (confirm('Stackoverflow FTW')) {
dfd.resolve();
} else {
dfd.reject();
}
}, 0);
return dfd.promise();
};
$('.container').on('click', '.another-page', function (e) {
e.stopPropagation();
e.preventDefault();
async().done(function () {
$(e.currentTarget).removeClass('another-page').click();
});
});
$('body').on('click', function (e) {
alert('navigating somewhere else woot!')
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="container">
Somewhere else
</div>
The reason I added the event listener to the parent and not the link itself is because the jQuery's on event will bind to the element until told otherwise. So even though the element does not have the class another-page it still has the event listener attached thus you have to take advantage of event delegation to solve this problem.
GOTCHAS this is very state based. i.e. if you need to ask the user EVERYTIME they click on a link you'll have to add a 2nd listener to readd the another-page class back on to the link. i.e.:
$('body').on('click', function (e) {
$(e.currentTarget).addClass('another-page');
});
side note you could also remove the event listener on container if the user accepts, if you do this make sure you use namespace events because there might be other listeners on container you might inadvertently remove. see https://api.jquery.com/event.namespace/ for more details.
We have a similar requirement in our project and this works for me. Tested in chrome and IE11.
$('a.my-link').click(function(e) {
e.preventDefault();
if (do_something === true) {
e.stopPropogation();
MyApp.confirm("Are you sure you want to navigate away?")
.done(function() {
do_something = false;
// this allows user to navigate
$(e.target).click();
})
}
})
I edited your code. New features that I added:
Added namespace to event;
After click on element event will be removed by namespace;
Finally, after finish needed actions in "MyApp" section continue propagation by triggering others element "click" events.
Code:
$('a.my-link').on("click.myEvent", function(e) {
var $that = $(this);
$that.off("click.myEvent");
e.preventDefault();
e.stopImmediatePropagation();
MyApp.confirm("Are you sure you want to navigate away?")
.done(function() {
//continue propogation of e
$that.trigger("click");
});
});
This is untested but might serve as a workaround for you
$('a.my-link').click(function(e) {
e.preventDefault(); e.stopPropogation();
MyApp.confirm("Are you sure you want to navigate away?")
.done(function() {
//continue propogation of e
$(this).unbind('click').click()
})
})