How to make one NOT be able to simulate a click - javascript

I've noticed while working for someone on a script that this special button ( the login one ): https://www.easports.com/fifa/ultimate-team/web-app/ does not allow to simulate a click on it in any possible way.
I'm extremely curious to know how they do it.
I've tried
var btn=$('#Login > div > div > button.btn-standard.call-to-action');
btn.click(); // or trigger('click');
// or
click = new Event(click);
btn.dispatchEvent(click);
// or
btn.trigger('mousedown');
// oh and also:
function click(x, y)
{
var ev = new MouseEvent('click', {
'view': window,
'bubbles': true,
'cancelable': true,
'screenX': x,
'screenY': y
});
var el = document.elementFromPoint(x, y);
el.dispatchEvent(ev);
}
I've even tried mouseenter, followed by mousedown and mouseup;
How can one achieve this sort of feature?

Here's an example of what they might be doing. Event.isTrusted gives you information on if it was a user action or a created event. They probably have some logic around this:
From the 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().
document.getElementById('btn').addEventListener('click', (e) => console.log(e.isTrusted));
// Simulate a click onload (should print false to the console)
document.getElementById('btn').click(); // false
// TODO: Click the Button manually, you should see 'true' being printed
<button id="btn">Button</button>
My guess is that if you look into their source code, you'd see something similar where they are just doing an .stopPropogation or .preventDefault if isTrusted is false.
So they are probably doing this:
document.getElementById('btn1').addEventListener('click', (e) => {
if (!e.isTrusted) { e.preventDefault(); return;}
console.log('Button clicked!');
});
document.getElementById('btn1').click(); // nothing printed to console.
<button id="btn1">Button</button>

Related

Actually do default after preventing default

I want to prevent default behavior of a link (a) then do the default behavior, let's say open open a link in a new window.
Here is some HTML code:
<a href="somewhere" target="_blank" id="mylink">
And the JS code:
document.getElementById('mylink').addEventListener('click', function (e) {
e.preventDefault();
axios.post('options', new FormData(document.querySelector('#myform')))
.then(function(){
// Here I want to do what the link should have done!
});
});
I know I can do something like this:
window.open(e.target.href);
But it's not an option because the browser consider this as a popup. And I don't want to rewrite something in JS, just consider the link as usual: this link has to do its default behavior (which was prevented).
Is there a way to do this?
Here is some idea:
var openingPopup = false;
document.getElementById('mylink').addEventListener('click', function (e) {
if(!openingPopup){
e.preventDefault();
axios.post('options', new FormData(document.querySelector('#myform')))
.then(function(){
// make sure this would not run twice
openingPopup = true;
document.getElementById('mylink').click();
});
}else{
// skipping this only for one time
openingPopup = false;
}
});
This way,
you run the popup opener click handler once,
then prevent the others,
trigger the click event again manually,
this time do nothing, but allow others to run.
As per #Electrox-Qui-Mortem 's suggested link, after you complete your pre-process(es), you can remove the event listener and call the click again.
(relevant MDN link: https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/removeEventListener )
const anchor = document.getElementById('anchor');
anchor.addEventListener('click', test);
function test(e){
e.preventDefault();
alert('test');
if( true ){
console.log( this );
this.removeEventListener('click', test);
this.click();
}
}
<a id="anchor" href="https://www.google.com">Test</a>
It's got wonky performance inside code testing tools (like JSfiddle and CodePen) -- you would have to test it in your actual application to make sure if it works appropriately for your use case.
I've only had good results with removeEventListener when referencing an outside function as the "listener" func. In this case test()
In a more "native" way you could create a new "click" event which is not cancelable and trigger it against the same element.
var anchor = document.querySelector('#target');
function triggerClick(target) {
var newClick = new MouseEvent('click', {
view: window,
bubbles: true,
cancelable: false
});
target.dispatchEvent(newClick);
}
anchor.addEventListener('click', function(e) {
e.preventDefault();
if(e.defaultPrevented) {
alert('Prevented!');
triggerClick(anchor);
}
else {
alert('Not prevented');
}
});
Click me!
Here the key is the cancelable: false in the new event created in the function triggerClick(target), which bypass the e.preventDefault().
In the embedded example on StackOverflow it doesn't work, but here's a JSFiddle!

Jquery disabled an event, and re activate it later

1 - I've gat an html tag with data-needlogged attribute.
2 - I would like to disable all click events on it.
3 - When the user click on my element, I want to display the authentification popin.
4 - When the user will be logged, I would like to launch the event than I disabled before.
I try something like the following code but it miss the "...?" part.
Play
<script>
// 1 - some click events has been plug on the tag.
jQuery('[data-btnplay]').on('click', function() {
alert('play');
return false;
});
// 2 - disabled all click events
jQuery('[data-needlogged]').off('click');
// 3 - Add the click event to display the identification popin
var previousElementClicked = false;
jQuery('body').on('click.needlogged', '[data-needlogged]="true"', function() {
previousElementClicked = jQuery(this);
alert('show the identification popin');
return false;
});
jQuery(document).on('loginSuccess', function() {
// 4 - on loginSuccess, I need to remove the "the show the identification popin" event. So, set the data-needlogged to false
jQuery('[data-needlogged]')
.data('needlogged', 'false')
.attr('data-needlogged', 'false');
// 4 - enable the the initial clicks event than we disabled before (see point 2) and execute then.
// ...?
jQuery('[data-needlogged]').on('click'); // It doesn't work
if (previousElementClicked) {
previousElementClicked.get(0).click();
}
});
</script>
Thanks for your help
Thank for your answer.
It doesn't answer to my problem.
I will try to explain better.
When I declare the click event on needlogged element, I don't know if there is already others click event on it. So, in your example how you replace the alert('play'); by the initial event ?
I need to find a way to
1 - disable all click events on an element.
2 - add a click event on the same element
3 - and when a trigger is launch, execute the events than I disabled before.
So, I found the solution on this stackoverflow
In my case, I don't realy need to disable and enable some event but I need to set a click event before the other.
Play
<script>
// 1 - some click events has been plug on the tag.
jQuery('[data-btnplay]').on('click', function() {
alert('play');
return false;
});
// [name] is the name of the event "click", "mouseover", ..
// same as you'd pass it to bind()
// [fn] is the handler function
jQuery.fn.bindFirst = function(name, fn) {
// bind as you normally would
// don't want to miss out on any jQuery magic
this.on(name, fn);
// Thanks to a comment by #Martin, adding support for
// namespaced events too.
this.each(function() {
var handlers = $._data(this, 'events')[name.split('.')[0]];
// take out the handler we just inserted from the end
var handler = handlers.pop();
// move it at the beginning
handlers.splice(0, 0, handler);
});
};
var previousElementClicked = false;
// set the needlogged as first click event
jQuery('[data-needlogged]').bindFirst('click', function(event) {
//if the user is logged, execute the other click event
if (userIsConnected()) {
return true;
}
//save the click element into a variable to execute it after login success
previousElementClicked = jQuery(this);
//show sreenset
jQuery(document).trigger('show-identification-popin');
//stop all other event
event.stopImmediatePropagation();
return false;
});
jQuery(document).on('loginSuccess', function() {
if (userIsConnected() && lastClickedElement && lastClickedElement.get(0)) {
// if the user has connected with success, execute the click on the element who has been save before
lastClickedElement.get(0).click();
}
});

Twitter Bootstrap. Why do modal events work in JQuery but NOT in pure JS?

The Twitter Bootstrap modal dialog has a set of events that can be used with callbacks.
Here is an example in jQuery:
$(modalID).on('hidden.bs.modal', function (e) {
console.log("hidden.bs.modal");
});
However, when I transcribe this method to JS the event 'hidden.bs.modal' does not work. Using 'click' does work:
document.querySelector(modalID).addEventListener('hidden.bs.modal', function(e) {
console.log("hidden.bs.modal");
}, false);
Are these Bootstrap events only useable with jQuery? Why?
Thanks,
Doug
The reasoning behind this is because Twitter Bootstrap uses that.$element.trigger('hidden.bs.modal')(line 997) to trigger it's events. In other words it uses .trigger.
Now jQuery keeps track of each element's event handlers (all .on or .bind or .click etc) using ._data. This is because there isn't any other way to get the event handlers that are bound (using .addEventListener) to an element. So the trigger method actually just get's the set event listener(s)/handler(s) from ._data(element, 'events') & ._data(element, 'handle') as an array and runs each of them.
handle = ( jQuery._data( cur, "events" ) || {} )[ event.type ] && jQuery._data( cur, "handle" );
if ( handle ) {
handle.apply( cur, data );
}
(line 4548)
This means that no matter what context is, if an event is bound via .addEventListener it will not run using .trigger. Here's an example. On load only jquery will be logged (triggered by .trigger). If you click the a element though, both will run.
$('a')[0].addEventListener('click', function(){console.log('addlistener');}, false);
$('a').on('click', function(){
console.log('jquery');
});
$('a').trigger('click');
DEMO
Alternatively, you can trigger an event on an element in javascript using fireEvent(ie) & dispatchEvent(non-ie). I don't necessarily understand or know the reasoning of jQuery's .trigger not doing this, but they may or may not have one. After a little more research I've found that they don't do this because some older browsers only supported 1 event handler per event.
In general we haven't tried to implement functionality that only works on some browsers (and some events) but not all, since someone will immediately file a bug that it doesn't work right.
Although I do not recommend it, you can get around this with a minimal amount of changes to bootstraps code. You would just have to make sure that the function below is attached first (or you will have listeners firing twice).
$(modalID).on('hidden.bs.modal', function (e, triggered) {
var event; // The custom event that will be created
if(!triggered){
return false;
}
e.preventDefault();
e.stopImmediatePropagation();
if (document.createEvent) {
event = document.createEvent("HTMLEvents");
event.initEvent("hidden.bs.modal", true, true);
} else {
event = document.createEventObject();
event.eventType = "hidden.bs.modal";
}
event.eventName = "hidden.bs.modal";
if (document.createEvent) {
this.dispatchEvent(event);
} else {
this.fireEvent("on" + event.eventType, event);
}
});
Finally change the Twitter Bootstrap line from above to:
that.$element.trigger('hidden.bs.modal', true)
This is so you know its being triggered and not the event that you're firing yourself after. Please keep in mind I have not tried this code with the modal. Although it does work fine on the click demo below, it may or may not work as expected with the modal.
DEMO
Native Javascript Solution. Here is my way of doing it without JQuery.
//my code ----------------------------------------
export const ModalHiddenEventListener = (el, fn, owner) => {
const opts = {
attributeFilter: ['style']
},
mo = new MutationObserver(mutations => {
for (let mutation of mutations) {
if (mutation.type === 'attributes'
&& mutation.attributeName ==='style'
&& mutation.target.getAttribute('style') === 'display: none;') {
mo.disconnect();
fn({
owner: owner,
element: mutation.target
});
}
}
});
mo.observe(el, opts);
};
//your code with Bootstrap modal id='modal'-------
const el = document.getElementById('modal'),
onHide = e => {
console.log(`hidden.bs.modal`);
};
ModalHiddenEventListener(el, onHide, this);
Compatibility:
https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver/observe#Browser_compatibility

Detect if button click real user or triggered by a script

Is there any method that enables me to detect whether a button click was performed by a real user and not some automated method (javascript) that a user has loaded onto their browser developer console or other browser developer tool?
I tried the following methods suggested in various stackoverflow posts but none of them appear to work.
REF: How to detect if a click() is a mouse click or triggered by some code?
Script Detection methods tried and failed:
mybutton.click(function (e) {
if (!e.which) {
//Triggered by code NOT Actually clicked
alert(' e.which - not a real button click')
} else if ('isTrusted' in e && !e.isTrsuted) {
//Triggered by code NOT Actually clicked
alert(' e.isTrusted - not a real button click')
} else if (e.originalEvent === undefined) {
//Triggered by code NOT Actually clicked
alert(' e.originalEvent - not a realbutton click')
}
// else if (!e.focus) {
// //triggered // does not detect e.focus on a real btn click
// alert(' e.focus - not a realbutton click')
// }
else {
// Button hopefully clicked by a real user and not a script
}
})
If I run the following script to trigger the button click from the Chrome browser console none of the methods above traps it as being triggered by a script.
var button = document.getElementById("btnSubmit");
button.click();
==========================================================================
Thank you for all your responses and thanks to stackoverflow for providing such a great site that facilitates so much knowledge sharing and for saving us techies an untold number of hours.
It appears that I still do not have reliable method. All 3 browsers (FF, IE & Chrome) provide a developer/console interfaces for a user to run/inject a javascript on my webpage. It appears that each browser flavor traps some event property values a little differently. For example: Chrome traps the difference between a script activated cick and a real user with e.screenX but in IE: e.screenX has the same value for both a script click (synthetic) and a user button click
The following detection methods either did not work at all or are inconsistent across the different browsers: e.which e.isTrsuted e.originalEvent (event.source != window) (e.distance != null)
The mousedown event appears to be only triggered by a real user button click, but I have to assume there is some script method to emulate a mousedown in addition to a button click event
$(me.container + ' .mybutton').mousedown(function (e) {
alert('mouseisdown real button click');
}
If anyone can figure out a reliable method that works across multiple browsers, that detects the difference between a synthetic (script) button click and a button click by a user, you will deserve superhero status.
when a button click happens through the mouse, the event e usually has the mouse pointer location recorded. Try something like :
if(e.screenX && e.screenX != 0 && e.screenY && e.screenY != 0){
alert("real button click");
}
No, it's not possible in all cases.
As other answers mentioned, you can look for the mouse coordinates (clientX/Y and screenX/Y), and if they're not present, you can assume it was probably not a human-generated action.
But, if the user tabs onto the button and uses the space bar to click it, or otherwise clicks it without using a mouse, the coordinates will also be zero, and this method will incorrectly determine it to be a scripted click.
Also, if the script uses dispatchEvent instead of click, coordinates can be given to the event. In this case, this method will incorrectly identify it as a user-generated click.
// This will produce what appears to be a user-generated click.
function simulateClick(element) {
var evt = document.createEvent("MouseEvents");
evt.initMouseEvent("click", true, true, window,
0, 110, 111, 10, 11, false, false, false, false, 0, null);
element.dispatchEvent(evt);
}
http://jsfiddle.net/bYq7m/
For security purposes when you trigger an event with javascript it will register differently than if the user triggered the event. Console log the ev object and you will see significant differences between the two cases.
mybutton.click(function(ev) {
console.log(ev);
});
Here are some sample output of the two cases:
jQuery.Event
currentTarget: button
data: null
delegateTarget: button
handleObj: Object
isTrigger: true
jQuery191011352501437067986: true
namespace: ""
namespace_re: null
result: undefined
target: button
timeStamp: 1360472753601
type: "mousedown"
__proto__: Object
jQuery.Event {originalEvent: MouseEvent, type: "mousedown", isDefaultPrevented: function, timeStamp: 1360472840714, jQuery191011352501437067986: true…}
altKey: false
bubbles: true
button: 0
buttons: undefined
cancelable: true
clientX: 39
clientY: 13
ctrlKey: false
currentTarget: button
data: null
delegateTarget: button
eventPhase: 2
fromElement: null
handleObj: Object
isDefaultPrevented: function returnFalse() {
jQuery191011352501437067986: true
metaKey: false
offsetX: 37
offsetY: 11
originalEvent: MouseEvent
pageX: 39
pageY: 13
relatedTarget: null
screenX: 1354
screenY: 286
shiftKey: false
target: button
timeStamp: 1360472840714
toElement: button
type: "mousedown"
view: Window
which: 1
__proto__: Object
Use "event.isTrusted" to know event is triggered from Javascript or User click.
Sample Code:
<!DOCTYPE html>
<html>
<head>
<title>Page Title</title>
</head>
<body>
<input type="text" id="myInput">
<button id="myBtn" onclick="callFunction(event)">Click Me</button>
<script>
function callFunction(event) {
console.log(event.isTrusted); // true only when user clicks 'Click Me' button
}
window.onload = function () {
let event = new Event("click");
callFunction(event); // false
}
var input = document.getElementById("myInput");
// Execute a function when the user releases a key on the keyboard
input.addEventListener("keyup", function (event) {
if (event.keyCode === 13) {
event.preventDefault();
document.getElementById("myBtn").click(); // false
}
});
</script>
</body>
</html>
Fiddle Here.
$("button").mousedown( function(e) {
if(e.which) {
alert(1);
}
});
$("button").trigger("mousedown");
Or Javascript:
document.getElementsByTagName('button')[0].onmousedown = function(e) {
if(e.which) {
alert(1); // original click
}
}
document.getElementsByTagName('button')[0].onmousedown();
You can see in the fiddle, it will not let trigger function to call but only when mouse is down on button. If automated click is triggered on button then e.which is undefined which can be trapped easily.
UPDATE : Fiddle For Javascript
Event object have a nativeEvent object which contains isTrusted field.
Its value will be false when event is not triggered by real user.
So you can check if real user clicked the button by -
if(event.nativeEvent.isTrusted){
//Real user
} else {
//Triggered by script
}

How do I cancel other onclick function calls further down the call chain?

I have an element with this onclick attribute:
onclick="ieDisabledBtn(); widgetPayTotalDue.show();"
I've added the first function, if appropriate, I do not want to execute the 2nd function. I basically need to tweak some things for IE (fake a disabled button by making it read-only, but not make it clickable). I can not use a disabled button as IE renders gray text for disabled button text. I can not use a disabled button.
Here is where I'm setting up my fake disabled button for IE:
(function() {
var $disabled_btns = $('button[disabled]');
if ($disabled_btns && $.browser.msie) {
$disabled_btns.each(function() {
$(this)
.removeAttr('disabled')
.attr({
'readonly': 'readonly',
'disabledIE': true //set a flag, used in ieDisabledBtn()
})
.click(function() {
$(this)
.removeClass('ui-state-focus') //don't want this button style
.find('span').css({ //need to shift text so it stays in same position
'position': 'relative',
'top': '-1px;',
'left': '1px;'
});
return false;
}); //end click
}); //end each
} //end if
})();
Here is my ieDisabledBtn function:
function ieDisabledBtn(e) {
//we only care about IE
if ($.browser.msie){
var e = window.event,
btn = e.srcElement;
//yes, we're a disabled IE btn, stop any other onclick events bound to this btn from firing
if (btn.getAttribute('disabledIE')) {
e.returnValue = false;
e.cancelBubble = true;
return false;
}
}
}
I thought for sure it'd just be a matter of setting e.returnValue = false and I've gone around several variations but I'm still getting that second function executed.
What am I missing?
I'm going to have these buttons spread throughout this app and they call different framework modals so I'm wanting to be able to just prefix those onclicks with a new function which will stop further onclick functions from executing if appropriate.
If you have two statements in your onclick event handler (ieDisabledBtn(); widgetPayTotalDue.show();) they will both be called. The onclick attribute roughly translates to:
function()
{
ieDisabledBtn();
widgetPayTotalDue.show();
}
So you need ieDisabledBtn() to return true when you want widgetPayTotalDue.show() to be called, which means:
function()
{
if(ieDisabledBtn())
{
widgetPayTotalDue.show();
}
}
or:
onclick="if(ieDisabledBtn()) widgetPayTotalDue.show()"
It's better to only have a single function to be bound to an event, for simplicity, if you can help it. Then you can make that function do whatever you like and keep it readable.

Categories