Button events and click depth - javascript

I have buttons which have been given click depth (i.e. they move down a few pixels on :active state) but they are causing problems in that sometimes the button is clicked and nothing happens.
<button>Button</button>
button:active {
    margin-top: 5px;
}
I have illustrated the problem in a jsfiddle:
http://jsfiddle.net/helenst/vUU55/
In Chrome, there is a thin strip above and below the text, height equal to the click depth, in which click events do not fire. (e.g. try clicking a pixel or two above the 'B') Both mousedown and mouseup are received, but click does not fire.
In Firefox and Opera, there is an area of the same size at the top of the button in which the mouse click does not respond. I find this slightly more logical in that the mousedown is inside the button but the mouseup is outside it. If I click down in this area but drag the mouse back into the button before releasing it, the click is activated.
However, it still doesn't completely make sense - if I wrap a container around the button (so that all button states are contained inside it), and detect events on the container, the problem still occurs.
If I remove the click depth, everything is fine.
I suppose I could make it respond to mousedown events and then detect mouseups on the document, this might get around it - but it violates normal button behaviour and I'd like to have a non-javascript solution. Can anybody help?

If you can make the :active style work using padding-top (or anything inside the border - even setting the border-top to 5px could work, sorry untested). Though of course that breaks your styling somewhat; a background top-aligned horizontal bar image could replace the top border if you need it in there, though a hacky idea).
Apologies, cannot test in newer browsers at present. I've seen this type of button several places before but cannot remember any offhand to test how well they cope with this issue/what code they use.
Almost made this a new answer, but SO is less fond of that:
The other alternative that I've seen, though harder to add really big depth (movement), is to use inset and outset in the border styles.

Related

mouseover fires inadvertently for image on layout change

I have a number of images in my HTML document.
The user triggers some changes (AJAX calls) when clicking on an image or by pressing a key.
To keep track of the latest image (client-side), I use javascript and onmouseover, assigning the image ID to a javascript-variable which in turn is used to fill the AJAX calls.
Everything works well (even it there might be better ways to do it), but sometimes it takes some time to refresh the image. For a moment, it ceases to exist, causing the image next to it to jump left.
This, of course, moves that image underneath the pointer, triggering the mouseover event.
How(?) is it possible to distinguish between a mouseover event caused by mouse-movement from one fired by a layout change?
The short answer is you can't really tell what caused a mouseover event, other than the cursor moved over a new DOM element (or some other things, like a tap on a touch device). A layout change is just something that happens independent of this event.
One possible solution to your actual problem is to avoid the problem altogether by preventing the layout jump. For example, you could use a placeholder element to fill the space while the next image loads.
Alternately, your logic could almost-certainly be made more-robust so as not to depend on something as fragile as this.

Limiting an editable div area by height, simulating onkeyup event in Javascript

I know there are many similar topics but none of them has the solution to my problem so please read my question carefully before sending similar topic links and marking as duplicate question.
I have a content editable DIV object, something similar to TextArea control. My goal is to cancel key press events if content starts scrolling and there must be no flickering.
When i use keyUp event, it's too late to cancel and there is also no methods available to cancel changes. What's done is done at this stage.
When i use keyDown or keyPress events, they are cancelable. But new changes are not yet applied. So, i know which character is pressed etc. but i still don't know how it's going to affect the scrolling size.
Plus, i allow style changes like making the text bold or changing the font size. Since there is;
document.execCommand("undo");
command, i'm able to test these changes and undo if scrolling starts. To test things, i use a cloned div with same content. It works fine. Changes are applied to cloned div (which is visible at the moment for debugging purposes but will be invisible if the method works) and if cloned div has an overflow, changes are canceled.
My problem is at doing the same thing for key presses. It's harder to simulate what happens to editable div content than using document.execCommand for other styling options. What i need is to get the innerHTML result at keyUp stage before keyUp occurs and event is still cancelable like keyDown or keyPress.
To simulate things, i tried getting cursor position and adding pressed characters manually using substring function but content isn't plain text and i had many problems with it. For instance when i press enter, an HTML block <div><br></div> is added for newline character which messed up cursor position. I tried many ways to handle things but it's very open to bugs. So, i decided not to follow this path.
In short my question is;
How can i possibly limit an editable div area by height, not allowing
to overflow or scroll without any flickering, just canceling key press
events? Do i have to simulate something like willKeyUp or is there any
other cross browser way?
Here is jsfiddle link for my sample which works for document.execCommand case (changing font size, weight etc.) but fails at typing letters;
http://jsfiddle.net/7zQD2/
Edit: To clarify my goal at jsfiddle example, after writing 5 lines of text, either when you press enter or type to end of the line, cursor should never reach to the sixth line. Key presses should be canceled and cursor should stay at fifth line with no content changes or flickers.
One solution is to use the cloning setup you already have, but to set the opacity of the first copy to 0 and position it on top of the clone with position: absolute in the css. The first copy won't be visible, but will catch a click directed towards the visible one underneath it. You can change your event to fire on keyup.
Since the transparent div still exists, and still has height, it can measure text height for you without being visible to the user. The visible text then updates to match what is learned with the transparent text, and never reaches the 6th line or flickers.
http://jsfiddle.net/7zQD2/2/

jQuery Hover Over Image Content Only and Not Alpha

Perhaps the title isn't great, but I had a little trouble with the wording...
Basically, is it possible to have jQuery's hover only fire over the actual content of a png with an alpha channel.
So in the image below;
http://i.imgur.com/3kO7v.png
Only fire on the rectangle and not the alpha channel which make up the rest of the document bounds...
I've tried the obvious $('obj').hover(function(){stuff}) but this fires on the alpha channel too...
The final implementation of this will be for more complex shapes than just a rotated square, so css3 tricks are out for the primary basis, but could be used with a back-up/shim, plus I need to support IE7 and ipad,iphone,ipod....
If there is a CSS2 solution then that would be suitable too. Also any real guidance on this issue is more than welcome.
My backup for this will be to have an empty div, display block and position it over the shape and then use that. This will obviously not be ideal for the less square objects.
Any hits or tips are more than welcome.
Thank you
Yes it is possible depending on the stacking context of your elements. Keep in mind that when you do a focus over any particular element on a page, that you are actually focusing all other elements within the same stacking context.
So what you could do is either stop the event from bubbling up the stack (if the element you want to "hover" is lower in the stack that the elements you want to prevent hover effects on), or specifically put in prevent default for onhover events for all elements in the stacking context except for the one you want to actually get a hover effect.

Selecting text behind another element with createEvent

I have a scenario where I have some text, which should be user-selectable. The problem is, that there's an UI overlay on top of it, which prevents selecting text by default. The logical way to keep the overlay and still be able to select the text, would be to use synthetic events (use document.createEvent), but due to some reason, it doesn't work as expected.
The events seem to be delegated correctly and fire their handlers, but no text is selected. I have an example here, which is a rough simplification of the problem.
A few notes
In Firefox if you start your selection outside of the overlay, you are still able to select the text you want, even if it's under the overlay
When you have a normal selection in the uncovered area and you click on the overlay, it would be expected from the delegated mousedown event to remove the selection, but it doesn't happen
Am I missing an event that should also be delegated (I have mousedown, mousemove and mouseup)? Or is it some kind of a security measure by browsers to disable such behavior (refer to the note nr 2)? Any other suggestions on how to get the desired result? I know I should work around the current overlay solution altogether, but I'm already curious about the problem itself.
I have found two solutions for this problem:
"pointer-events" css property. Requires IE 9.0+ though.
Seems like guys from ExtJS solved it by event forwarding: demo, source, blog post
I would suggest to do the easy trick: put the transparent element with the same content as your text on-top of text itself and overlay. Here is the demonstration.
P.S.: From my experience any solution in the form you suggest will be awful. It will suffer from browser incompatibilities, side-effects of surrounding mark-up and styling etc.

Why can't I reliably capture a mouseout event?

I need to know when the mouse cursor leaves a div. So I hook up the mouseout event. However, if I move the mouse very quickly out of the div, the mouseout event doesn't fire. That's right: the mouse cursor was sitting still inside the div, it's now outside the div, and yet the mouseout callback hasn't been called. (It works fine if I don't move the mouse quite so fast.)
This is true in the latest Google Chrome by the way – so not just an "old browsers" problem.
A workaround:
A question about this problem has been posed before. Apparently it's just a fact of life, and the only workaround I've found is to manually monitor mousemove events, each time checking the cursor's x/y co-ordinates and seeing if they fall within the div’s bounding box, so you have more chances to "notice" if the cursor is no longer inside it.
Compared to letting the browser do all this natively, performing calculations on every pixel move is a bit of a performance hit. It's also tedious to code.
On to my question...
Why can't the browser can't reliably capture the mouseout event? If I can reliably tell when the mouse has left the div using the above workaround, why can't the browser do it?
I understand (from the answer linked above) that JavaScript doesn't try to interpolate "frames". Say if you put a mousemove handler on the document, and quickly move the mouse 200 pixels to the right in a perfect horizontal line, you might not get 200 mousemove events. A few will be missed. I don't have a problem with that.
But if some pixel movements are missed just as the mouse crosses the boundary of the div, why does it follow that the mouseout event should also be skipped? When the browser finally starts registering the mouse's position again (after a sudden fast movement), even if the mouse is now miles outside the box, the point is that it used to be in the box and no longer is. So why doesn't it then fire the mouseout event then?
I just don't get why this would be a hard problem for the browser vendors to solve. (But I trust there might be a good reason, which I'm too stupid to think of.)
I'm posting this question mainly out of curiosity, but I'm hoping the answer might give some insight that could help me work around the problem more efficiently. Also, any alternative workarounds (which are faster than the one presented above) would be welcome.
I know that you don't want a workaround, but you don't need to check mouse's x/y to know if you are in or out an element. You could simply check the element from which the mousemove event was fired. If you put a mousemove on document, the event will fire from one of its children, and you can compare that element with your element to know if it is one of its descendants.
Or you could go up the parentNode tree and stop if you find your element. Then you know you are inside the element and still in it, otherwise you reach the document and you are out.
Some browsers implement the mouseenter/mouseleave events that, I've noticed, are more accurate than mouseout. Prototype and jQuery have a workaround for browsers that don't implement these new events. Mouseleave does not fire from an element's children, whereas mouseout does.
You describe moving the mouse very quickly. When you stop, is the pointer still within the page? That is, is your mouse pointer still hovering over some part of the visible web page?
If it has gone outside, then it's not necessarily clear what the browser should do. The mouseout event should have a relatedTarget property that targets what the mouse pointer has gone into. If the mouse pointer is already outside of the page area, there would be no related target to point to.
Put another way, when the mouse leaves the page area, the browser stops tracking it and stops reporting its position. If you move your mouse fast enough, from the browser's perspective, the mouse simply disappeared. It's not until you bring the mouse back into the bounding box of the viewable page that the browser knows where it is, and then triggers all appropriate movement-based actions (like mouseout).
Why can't the browser can't reliably capture the mouseout event? If I can reliably tell when the mouse has left the div using the above workaround, why can't the browser do it?
I think you answered this one yourself when you said:
Compared to letting the browser do all this natively, performing calculations on every pixel move is a bit of a performance hit.
The browser does not interpolate between the frames, thus, as you stated it would demand a lot more resources and memory, which may be why it isn't "fixed".
If some pixel movements are missed just as the mouse crosses the boundary of the div, why does it follow that the mouseout event should also be skipped? When the browser finally starts registering the mouse's position again (after a sudden fast movement), even if the mouse is now miles outside the box, the point is that it used to be in the box and no longer is. So why doesn't it then fire the mouseout event then?
I don't know for sure, but I don't think it's a condition of "it was in and now it's out". Instead, it's whether it crosses that boundary (if MouseX - ElemOffsetX= 1). I agree, it doesn't make as much sense, but it could be because if you set the value to > 1 it would trigger the event multiple times. Otherwise it would have to keep track of the events, which is not in JS nature, seeing how it just adds events asynch to a stack.
What you could try is using jQuery's mouseleave event. This does two things, which delays the firing of the event:
It traverses the DOM tree to see if it truly left the element
I think it implements a timeout call, which should solve the interpolation problem that you noticed.
I found your question and the lack of other clear answers useful because it told me that I had to create a workaround. Which I did using the ideas presented in your question and the other contributors.
I have same problem when I use jquery mouseleave elem.bind('mouseleave', data, mouseLeavesZone);
The problem is intermittent and may be related to a busy CPU on the client. Say, for example, the CPU is busy elsewhere when your mouse moves out of a div. Then it seems logical that this could be the cause of the bug. I agree; this should be fixed by the browser vendors.
See http://jsfiddle.net/bgil2012/gWP5x/1/
(Aside: My JQuery code needs to use older jQuery methods because it has to run in Drupal 7 which is running jQuery 1.4, at this time and without applying patches that are coming).
I ran into this problem a few times and I came to accept the issue as a fact of life. But depend on your needs, you can just use CSS as I did. For example, if I just want to show/hide an element base on another element got hovered, then CSS is the way to go. Here is a working, reliable example:
.large {
width: 175px; height: 175px;
position: absolute;
border-radius: 100%;
/*hide the glass by default*/
top: -9999px;
left: -9999px;
opacity: 0;
transition: opacity .2s ease-in-out;
z-index: 100;
pointer-events: none;
}
.small:hover + .large {
opacity: 1;
}
http://codepen.io/tanduong/pen/aBMxyd
Here's a simple workaround.
In your onMouseOver listener, you can add a 'mousemove' listener to the window:
<div onMouseOver={() => {
setMouseOver(true)
let checkMouseLeave = (e: MouseEvent) => {
if (rootRef.current
&& !rootRef.current.contains(e.target as HTMLElement)) {
setMouseOver(false)
window.removeEventListener('mousemove', checkMouseLeave)
}
}
window.addEventListener('mousemove', checkMouseLeave)
}
></div>
Then you can check on each mouse move until the mouse is outside of your div (rootRef.current in our example).

Categories

Resources