How can I stop the effects of the users clicks? - javascript

I am making a chrome extension to edit properties of images that are clicked on. I am using a package called element picker to select the images (this is triggered through an html button in a popup). The code works and I can change the properties of the image. However the package does not stop whatever action is linked to the image, which can often lead to the user being taken to a new page. How can I stop any of the actions of the users click between the time they press the button in the popup and they have selected an image?
Thank you in advance.
var elementPicker = require('element-picker')
function onClick(elt) {
[.....]
}
elementPicker.init({ onClick })

I don't usually recommend using this but CSS pointer-events can solve this problem. The idea is that any element with pointer-events:none will ignore any interactions. This works to block default HTML interactions like <a> or <button> as well as any javascript actions attached to the element.
This is the technique frequently used with a modal window to prevent clicks from going "through" the area around a modal. It should also work for what you described.
You could either set that style on the image element or on All Elements by using the * {styles...} selector. If you go the "all" route, you'll need to explicitly re-enable pointer-events on any elements in your extension interface that you still need actionable by using the 'auto' property.
Remember to reset pointer events when your extension is finished * {pointer-events: initial;} or you'll leave the page completely in-actionable.

If there is an Event object being passes through to the function then you could use the Event.preventDefault() function.
This function stops any default behavior and allows you to handle the event in your own manner.
Reference: https://developer.mozilla.org/en-US/docs/Web/API/Event/preventDefault

Related

How to get actual element clicked on instead of Rails unobtrusive javascript form?

I have a small React 16 menu panel that is running in a Rails 6 application. When the panel is open, I want to close it and stop propagation, if the user clicks outside the panel. (I don't want an accidental click outside the menu to take the user to another page.)
The general approach is quite well understood - here's one question that describes it. In brief:
Using native JavaScript (i.e. document.addEventListener('click', callback)) inside of the React component, listen for the click event anywhere.
When that click event fires, check if the element (i.e. event.target) is within the div of the React menu.
If the click was outside of the React menu, close the menu, and call event.preventDefault() and event.stopPropagation() to stop accidental navigation.
This all works fine in my simple test case. (Here's a fiddle, just to prove it works.)
However, it fails in my nested Rails application, if the user clicks on an anchor tag, because Rails's Unobtrusive JavaScript adds listeners to all anchor tags that contain the data-method attribute. The unobtrusive JS then hijacks the event, and injects an HTML form into the body. The target of the click event becomes an input of that new form. You can see the form being dynamically created when I click on a link in the panel here:
This means that the event.target property is the input in this dynamically-injected form, and not the actual a tag the user clicked on.
So my question is: How can I get the actual element the user clicked on, which triggered the creation of the form.
Stuff I've tried/thought of:
Not using data-method. This works for the links in my menu. But then I cannot do POST events like logout, and any links created with Rails's helper methods.
Listening for onmousedown instead of onclick. In this case, the event's target is the correct element, but calling preventDefault() doesn't actually prevent the click.
Using forms inside my menu component instead of anchor tags. This way, I can do a POST for the logout action, without having to use the data-method attribute which introduces the problem described here. And it just works here, since if I click a link in the Rails app, I know it's outside the menu. But it's a hacky workaround, and it doesn't give me flexibility if I want to listen to some other clicks in the future.

How to fire mouse event when page loads without moving the mouse?

I have a link to my homepage that changes color when the mouse gets over it. When the user clicks, however, the page refreshes and the link turns back to the old color. I want the link to change to the new color again just because the mouse is there, but no mouse event is being fired with the mouse stopped. In JSFiddle, however, it works (putting the mouse over the text and refreshing all the page with F5), so I guess there must be a way. I don't want to use css :hover because the color is changed often, and I've found that changing a css value in javascript is kind of a nightmare. Or isn't it? Also, no JQuery, please.
function colorize() {
this.style.color = '#C83';
}
function load() {
var p1 = document.getElementById('p1');
p1.addEventListener('mouseover',colorize);
}
p {
font-size: 2em;
background-color: #385;
}
<body onload='load()'>
<p id='p1'>Some big text</p>
</body>
I gotta be honest with you, this scenario of yours
When the user clicks, however, the page refreshes and the link turns
back to the old color.
seems more like an issue of force refreshing your page. Why are you forcing a refresh?
Based on this thread,
You can't. It's not a trusted event.
Events that are generated by the user agent, either as a result of
user interaction, or as a direct result of changes to the DOM, are
trusted by the user agent with privileges that are not afforded to
events generated by script through the
DocumentEvent.createEvent("Event") method, modified using the
Event.initEvent() method, or dispatched via the
EventTarget.dispatchEvent() method. The isTrusted attribute of trusted
events has a value of true, while untrusted events have a isTrusted
attribute value of false.
Most untrusted events should not trigger default actions, with the
exception of click or DOMActivate events.
You have to add a class and add/remove that on the mouseover/mouseout
events manually
There's just no standard/worth mentioning native way to do it.
You would have to retain the state somehow when refreshing. Passing a query string ?isMouseOver=true and parsing it back after the refresh should suffice. Just execute the same mouseover function after verifying the query string, no need to programmatically fire a mouseover event.
Or you can use jQuery, which has trigger-able mouse events.
But I would honestly avoid that force refresh solution and come up with something else

javascript how to determine what is cancelling an event

I have jquery, bootstrap included in a page I'm writing. It's a complex page. The problem I'm having is with Internet Explorer not seeing mousedown event. Chrome and FF both see the event just fine but not IE.
I wrote a test page with the event and it worked just fine in IE. So my question is...
Is there a way through the developer tools to determine what is cancelling an event?
I have a suspicion that one of the many .js files I've included is cancelling the mousedown event and IE isn't seeing it anymore. Chrome and FF does though. So I'm not 100% that it's being cancelled but it's my only guess I can come up with.
Code is really irrelevant since it's all of jquery and bootstrap. However, I am playing with divs that are draggable and resizeable. That's why I need to use jquery. The bootstrap is used because I also have a wysiwyg editor on the page.
Please don't recommend click. I need mousedown. When the mouse is down the border around the draggable and resizeable div turns red and I have some code that selects that div to capture top, left, width, and height as it's being moved and resized.
If click was selected as the event, the user would have to click the div box first then click and hold to move it. That's not a user friendly interface.
Any help is greatly appreciated.
What do you exactly mean as cancel, .preventDefault() or .stopPropagation? If we are talking about preventDefault - you should still be able to add event listener to parent container and then see your event object - it might have some data to traceback. Alternative would to override jQuery .on method and see who actually subscribes to the event.
After little more thinking - add another listener BEFORE the malicious one, to do that insert document-ready handler with event binding right after jquery loading code. In your new mousedown handler try to override problematic method of the event.
UPDATE:
you should try to check all events attached to your element one by one. To do that - check this post jQuery find events handlers registered with an object
In short - try using jQuery._data( elem, "events" ); to see attached event listeners and inspect their code in your code base. After you find the reason it will be much easier to reach the desired functionality. Before that it is just a guesswork.

How do I make links with no href attribute accessible?

A third party script is being used on a site I work on that replaces a few instances of <a href=""> with <a>. The links still work thanks to another part of the script, but they are no longer treated as links by user agents.
I can restore them to the tabbed navigation order by adding tabindex="0" but how can I make assistive technologies announce them as links or include them in a list of all links on a page?
Would adding role="link" help at all?
I am pushing the third party to improve their script so that the href is left intact. But in the meantime how do I best repair the damage that's being done?
I can't add either the original href or something like href="#" back to the links as the third party code will no longer do what it does. I hope that they improve their code so that I can, but for now I need to make the link accessible without the 'href'.
To make a non-href <a> behave like an <a> (and be accessible), you'd have to add role=link, tabindex=0, style it to look like a real link, and add keyboard handler code to treat Return as a click.
role="link" isn't sufficient; a screenreader may report it as a link, but without tabindex="0" and appropriate visual styles, a sighted user won't be able to tab to it in the first place, and without a keyboard event handler, only mouse users will be able to click it. (Technically screenreader users typically have hotkeys to simulate a mouse click, but keyboard-only sighted users generally don't have that option, so don't rely on it.)
Alternatively, if (big if!) the crazy script you're using allows for it, you could try shimming a 'keyboard click source' (my terminology) <a> just inside the original one: so where you have:
<a>foo</a>
you replace it with:
<a><a class='shim' href="javascript:void(0)">foo</a></a>
(The class='shim' is only needed if you need to do the event stuff described later...)
You can do this in jQuery using something like: (borrowing from Jack's answer)
$("a:not([href])").wrapInner("<a class='shim' href='javascript:void(0)'></a>")
How this works is that the inner newly-added <a ...> has a href, so it is exposed as a link and is tabbable. More importantly, if a user tabs to it and presses return, the default A behavior converts that keyboard input into a click event. This specific A has a href that returns undefined/void(0), so no actual navigation happens, but the click event will still bubble up to the original A, which gets to act on it.
(This is a neat pattern for allowing some parent element - often a DIV or similar - to handle click events, adding a child tabbable A that can source click events from keyboard gives you UI that's both mouse and keyboard usable.)
The big caveat here is that it assumes that your original script doesn't care about the target of the event. If that script does check this, it will get confused when it sees click events coming from the shim A's rather than the original As. One way to get around this is to capture and re-raise the event, which can be fiddly, and may only work on recent browsers - eg using something like:
// 'shim' class used so we can do this:
$("a.shim").click(function(e) {
e.preventDefault();
e.stopPropagation();
// the following works if listener using jQuery or is setting onclick directly, otherwise...
// $(e.target).parent().click();.
// More general way to raise events; may need alternate for IE<9
var e2 = document.createEvent("UIEvents");
e2.initUIEvent("click", true, true, window, 1);
e.target.parentNode.dispatchEvent(e2)
});
Whilst it's not very pretty, you can get at all anchors without a href attribute like so, using jQuery;
$("a:not([href])")
You can then just set the href attribute on those links to "#" and that should make them work again as regular links.
Here's a working JSFiddle
Sorry to reply with a jQuery solution...but doing this in regular JavaScript would be much more verbose.
Another way would be to give the anchors a role and then select them that way:
$("a[role='link']")

What's the most efficient way to handle displaying a dialog/modal in JavaScript?

[UPDATE:] here is a link to test (if you don't want to clone the repo) http://jsfiddle.net/integralist/g9EPu/
I've got a lot of dialogs/modals that need to be displayed when mousing over certain links in a web app.
Table of content (tl;dr)
How I used to do handle it
How I've tried it recently
Which is better?
What about mouseenter/leave?
How I used to do handle it
The way I usually do this is to use event delegation.
So I add one event handler to a container and then check for the relevant element to become the target and then display the relevant dialog.
I normally have one dialog which I change the content for and re-position (saves having lots of different HTML mark-up).
If the mouseover event (for the link) gets triggered then I display the dialog.
If the mouseout event (for the link) gets triggered then I hide the dialog.
If I mouseout of the link which triggered the event handler then I normally need to set a timer to delay hiding the dialog (just long enough) so I can then mouseover the dialog which itself clears the timer set by the mouseout of the link.
I then have a mouseout event bound to the dialog so I can then hide the dialog when the user rolls their mouse off the dialog.
There are two problems I've encountered at this stage, the first happens practically all the time and the other is an edge case I noticed recently which prompted me to try and find a better solution...
The dialog has 'x' number of child elements and rolling the mouse over a child element causes the mouseout event for the dialog to be triggered hence I need to put in checks to see if the element has a parent which is the dialog itself and if so then don't try to hide the dialog.
When using this technique on a <table> element I've found that when the mouse moves too quickly the mouseout/over events don't get triggered.
How I've tried it recently
For example code see: https://github.com/Integralist/Mouse-Over-Out-Script (you should be able to just clone the repo and run the index.html file locally to see what's happening)
But to give a brief explanation...
We bind a mousemove event to the document.documentElement element (but you could do it on the document.body if you wanted) and then we store the x/y co-ordinates of the mouse position. We provide public API access to a 'check' method which lets us know if the position of the mouse is over the element we've provided to 'check' (we measure the elements dimensions and add those onto its x/y co-ordinates).
In the above repo we have a calendar which shows a dialog whenever a particular date has an event on. We're storing all <td>'s that have an event and we set-up a timer for each of those <td>'s (this is because we need to keep calling the 'check' method to see if that <td> has the mouse over it).
So potentially there could be 31+ (because we're showing the first few days of the following month) opportunities for a dialog to be shown and so 31+ timers set!
This example repo works now, where as the first version where I was using event delegation wasn't.
Which is better?
I'm worried about performance on the mousemove version because it can potentially use a lot of timers (depending on how many dialogs you need in a single page). In my calendar example above there is up to 31+ timers that could be running!
What about mouseenter/leave?
I know these events exist and if all browsers supported it then I could safely use the first version and not have to check for child elements causing erroneous mouseout/over events to be triggered. But regardless I don't believe this would have fixed the example with the event calendar where moving the mouse too quickly was meaning the mouseout/over events for the <td>'s weren't being triggered by the browser. Either way, I know you can polyfill this as jQuery provides mouseenter/leave events but looking through their code I couldn't get that to work for my script (as I don't use jQuery or any other general purpose library - ps, and I don't wish to, so please do not suggest that as an option).
Many thanks for any help/advice or guidance someone can provide me.
The dialog has 'x' number of child elements and rolling the mouse over a child element causes the mouseout event for the dialog to be triggered hence I need to put in checks to see if the element has a parent which is the dialog itself and if so then dont try to hide the dialog.
To solve this: in your event code, simply use the function "isAncestor" (see below)
/*
* element = the "target" in your mouseout event handler
* other = the node you really want to check if you're over
*/
isAncestor: function(element, other)
{
while ( element && element != other ) element = element.parentNode;
return ( element != null && element != undefined );
}
So in your mouseout code for your element (let's call it "itemElement"), you'd check it like:
//We're really mousing out, close dialog
if ( !isAncestor( mouseOutEvent.target, itemElement ) )
{
...do something ...
}

Categories