Polymer 1.5/iOS: How to stop event propagation over iron-pages - javascript

We have a one page app which uses iron pages and express-router to navigate. In the browser and on android it works perfectly, on iOS however, we have a bug. The problem occurs if we switch pages by a button press. If the button is exactly over an input text field element (read: in the same position, but on the next iron-page) the input element gains focus directly after the page switch.
We used to have this problem as well with two buttons in the same position but this was solved by changing all on-clicks to on-taps.
Things we have tried so far:
Adding event.stopPropagation to the on-tap event
Including fastclick.js to prevent click delays (this worked partially when on-clicks were still in place but was made obsolete with on-tap)
Note that we have experienced this problem since Polymer 1.0 all through 1.5.

I reproduced your symptoms on an iPad Air 2, and trying e.stopPropagation(), e.preventDefault(), and returning false all had no effect. I'm not sure whether this is actually a Polymer problem.
I have a couple [hacky] workarounds to employ during the page-switch:
Option 1: Delay the page-change by 400ms. If your button has a ripple effect, the delay is masked by the animation.
codepen
Option 2: Disable the input and re-enable it after a 400ms delay. This prevents the input from picking up the tap event, but has the disadvantage that the disabled state could be noticeable (perhaps a lesser evil than your current problem).
codepen

Thanks #tony19, for the input.
We wanted to avoid delays, so I researched a bit more and ultimately fixed the problem. To answer my own question: the ultimate solution did lie in the FastClick library.
Basically what happens is that the tap event is fired immediately, but it doesn't replace the click event. Rather, the click event still fires, but with the original 300ms delay. This delayed click event thus fires on the newly displayed 'page' and triggers the input-field (or button had it been there) at the same x-y coordinates.
Adding the FastClick library once again solves this thanks to some updates in the library. However, it breaks some items that need the original click, such as Google Autocomplete. A basic solution to exclude FastClick is to instead apply it as:
FastClick.attach(document.body, {
excludeNode: 'something', });
This, however, only works for that node and not possible children. As such, to fix everything for input fields with Google's Autocomplete as well is done using:
// Disable FastClick for the children of a google auto-
// complete component.
var needsClick = FastClick.prototype.needsClick;
FastClick.prototype.needsClick = function(target) {
if ( (target.className || '').indexOf('pac-item') > -1 ) {
return true;
} else if ( (target.parentNode.className || '').indexOf('pac-item') > -1) {
return true;
} else {
return needsClick.apply(this, arguments);
}
};
// Remove click delay on iOS. As such, removing differences
// in timing between click and tap, thereby avoiding the
// fall-through problem.
FastClick.attach(document.body);
I will now close this thread, but I thought it'd be nice to leave this as reference for anyone else experiencing the problem.

Understand that it affected Polymer 1.0 to 1.5. Just to confirm that we experienced the same behaviour in Polymer 1.6 and the following fixes it.
_onTap: function(event) {
event.preventDefault();
event.stopPropagation();
}

Related

input[type=number] continuously increasing when clicking on arrows in Chrome and Edge

I am experiencing the following issue: After a particular jQuery library loads, if you click on a number input arrow, the input value keeps increasing (or decreasing) until the focus is shifted outside the input element.
Binding an input event to the element showed it keeps triggering, which led me to believe some piece of code kept setting element.value in a loop. But that did not happen.
I've tracked the issue down to calling event.preventDefault() on a mouseup event.
See:
document.body.addEventListener('mouseup', (e) => {
e.preventDefault();
});
<input type="number">
Why does this happen?
I had trouble finding information by searching what causes these infinitely increasing inputs.
Only after finding the cause myself, I've found out about a similar bug that happened with preventing the default of mousemove (https://stackoverflow.com/a/37521764/6849064). Although, this one does not seem to happen anymore.
Looks like this is a Chrome (and Edge) bug. But as https://stackoverflow.com/a/65253876/6849064 said, it is actually default behavior which makes sense in the way he said it. I myself failed to find this standard behavior documented anywhere. One way to fix the problem is to stop bubbling of the event up to the document,
document.querySelector('input').addEventListener('mouseup', (e) => {
e.stopPropagation();
});
And the other is, well, not preventing default behavior.
The following issue has nothing to do with jQuery. The issue is the code is doing exactly what you wanted it to do.
In the code <input type="number" /> has got 2 events. One is 'mousedown' and second is 'mouseup'.
Consider following example: https://www.w3schools.com/jsref/tryit.asp?filename=tryjsref_event_preventdefault2
The same is represented in the example above.
What you are doing is cancelling the 2nd part, ie mouseup so if you do mousedown and cancel mouseup, then the number will:
Go on increasing if you push uparrow
Go on decreaseing if you push downarrow
The only, surprise is this would have perfect sense if you would have written the code adding event to input rather than body, but anyways seems browser by default is increasing or decreasing number -- based on event-bubbling to body.
Note: The issue is replicated below, while no jQuery has been added!
document.body.addEventListener('mouseup', (e) => {
e.preventDefault();
});
<input type="number">

Cannot get focus to return to div after input blurs

http://jsfiddle.net/NsRyr/1/
I am totally stumped on this. Here's what I'm trying to do : I have a div element (call it #keys) that I'm using to handle keypress events.
HTML:
<div id="#keys" tabindex="1">Focus</div>
JS:
$('#keys').keydown(function () {
$('#log').append('*'); // just note that an event happened
});
This works as expected -- as long as #keys is focused, I can receive keypress events and respond to them. In addition, I can set focus to other div elements and the keypress events will no longer be handled by #keys. I can then re-focus the div (e.g., by clicking on it directly, or by responding to a click event on another DOM element) and keypress events are handled as I expect.
So far, so good. The problem that I've come across is that if I focus an input element and then try to re-focus #keys by setting a blur handler that's activated after tabbing away from the input, the #keys div does not receive focus ! It works fine if I blur away from the input by clicking, but not if I use the keyboard.
$('#input').blur(function () {
$('#log').append('blur'); // note that a blur happened
$('#keys').focus();
});
I think the essence of this question is, why doesn't my blur handler on #input seem to work properly when tabbing away from the input (but it does work properly when clicking away) ? Is this just a browser quirk ? (I am using Chrome 30.0.1599.101 on Mac OS.)
This is my first SO question regarding JS, so please let me know what additional detail I can provide to describe the situation.
(Edit : Interestingly, it seems to work fine if I shift-tab away from #input. Still confused what's happening here ; appears to be some sort of tabindex-related issue ?)
I don't have commenting privileges yet, so I'll have to answer, but please mods should change this, because it's basically a duplicate.
Here's the fix to your fiddle:
http://jsfiddle.net/NsRyr/3/
The issue (as described in this answer) is that the blur event fires before the change is done, so the focus needs to be sent down the stack to happen after. Adding a timer deals with the issue.
The line I changed was:
setTimeout($('#keys').focus.bind($('#keys')), 0);
That makes it so it'll wait until the new focus event is completed before firing off the handler.

jqGrid: Column resize triggers click event

First of all, these are the versions I am currently using:
jqGrid 4.3.2 with the fix for Chrome (posted by "Oleg" in jqGrid does not render correctly in Chrome/Chrome Frame). For some reason 4.3.2 and 4.4.0 did not solve the width issue for me as described in the post. The issue just popped up in IE in addition to Chrome.
jQuery 1.7.2
jQuery UI 1.8.9
The problem I am having is that when I try to resize one of the columns in the grid by dragging the mouse it seems to trigger the click event on the header to the left of the separator when I let go of the mouse button. This event then triggers reordering of the rows, so it is not very nice.
It only happens in IE (9), it works fine in Firefox and Chrome.
I think this is very strange, since I have not found anyone else who describes the same issue with jqgrid, and I don't think I do any "hacks" that would potentially give this behaviour.
Hope someone could point me in a direction here.
I did not find exactly what the root cause for my problem was, but managed to solved it by suspending the click handler in jqgrid for 10 milliseconds after the mouseup event on the column resize action. The click handler allready had a check for a variable called ts.p.disableClick, so I figured I might as well use this one. The only thing I needed to change was from this:
$(document).mouseup(function () {
if (grid.resizing) { grid.dragEnd(); return false;}
return true;
});
, to this:
$(document).mouseup(function () {
if (grid.resizing) {
// Disabling the click handler for 10 millisec.
ts.p.disableClick = true;
setTimeout(function() {
ts.p.disableClick = false;
}, 10);
grid.dragEnd(); return false;
}
return true;
});
You may call this a hack, but suspending the click handler for just 10 ms should not affect the user in any way, so I think it should be safe.
Hopefully this could be helpful if someone encounters a similar problem.

jQuery mouseout issue on iPad [duplicate]

I have a jQuery code which works perfect on desktop browsers;
$("span#checkbox_err").mouseout(function () {
$("span#checkbox_err").fadeOut("slow");
});
But the same does not trigger on the iPad (as a result the checkbox_err is displayed on screen, but never hides)
How do I trigger the mouseout event on the iPad ?
Also I'll want to avoid using any additional library just to fix this small issue..
I HAVE A FOLLOW UP QUESTION
I am testing a page on iPad and am facing some issues implementing an equivalent of mouseout behavior..
So the issue is very simple to understand; 1. On my page, there is a checkbox on click (or rather touch), I want to show an errorMsg 2. On click/touch on anything other than the errorMsg, I want to hide the errorMsg
Below is the code I have written;
$(document).bind("touchstart",function(e){
if(e.target.id != "checkbox_err")
$("span#checkbox_err").fadeOut("slow");
});
}
$("input:checkbox").bind("touchstart",function(){
$("span#checkbox_err").fadeIn("fast");
});
Now the issue is when I click/touch on the checkbox, the errorMsg shows for a while and then it also hides it immediately (since target is not the errorMsg)
How do I fix this issue?
You could try .blur() instead of .mouseout()
Maybe because of bubbling?
It makes sense to me, the event will reach the underlying layer which is not the target.
So you have to stop eventPropagation:
$("input:checkbox").bind("touchstart",function(){
$("span#checkbox_err").fadeIn("fast");
event.stopPropagation.
});
Hope it helps ya.
Did you happen to find an alternative for mouseout? - which brought me here.
this example will surely help you ! http://jsfiddle.net/PzTcS/12/, It works well on iPad.
You could try with GestureEnd() event in ipad

jQuery find(':focus') not acting as expected

I'm making a widget that slides in and out of view on hover with showTracker and hideTracker functions. I want to prevent it from sliding out of view if it contains a focussed form element though, so I've got this going:
function hideTracker(){
if($('#tracker').find(':focus').length == 0){
$('#tracker').stop().hide();
}
}
Cool. Now it doesn't hide if the mouse happens to move out if there's a field in focus. Unfortunately, that also means that when the field does lose focus (and it's time for the widget to hide again) it just stays there. The unHover event has been and gone.
So I added this:
$('#tracker *').blur(function(){
hideTracker();
});
And that works too - with one little bug that I need help with!
If the focus moves from one element within the tracker to another which is also within #tracker, the tracker hides. I figured that if($('#tracker').find(':focus').length == 0) would return false, given that the next form element has focus, but I guess it doesn't.
Is it the case that .blur() fires before the next element attains focus?
How can I get around this?
How about something like this?
$('body *').focus(function(){
if(!$(this).is('#tracker *') && $('#tracker:visible').length != 0) hideTracker();
});
Yikes. Tricky. Yes, what's happening is:
mousedown: old form element gets the blur event. $(':focus').length == 0.
mouseup: new form element gets the focus event. $newFormElement.is(':focus') == true.
This is an improvement:
$('#tracker').focusout(function() //basically like $('#tracker, #tracker *').blur(), but "this" is always '#tracker'
{
if(!$(this).is('#tracker:hover')) //for some reason plain old :hover doesn't work, at least on the latest OS X Chrome
hideTracker();
});
But it's not perfect. It only really works if you use the mouse. If you use tab to move between fields (or some other possible mechanism) while your mouse is not hovering over #tracker, it won't work.
Here's another attempt. It's a bit...hackier. The gist is that, instead of handling the blur event, you handle the focus event of the second thing that's focused. But! What if you click something that can't be focused? Blank space on your page? Then no focus event is fired.
Okay. So the trick is: put a tabindex="0" in your root <html> tag. This means that there is always something that can be focused. So there's no way to focus on nothing (at least, I don't think so).
Then you can do this:
$('*').live('focus', function(e)
{
if(!$.contains($('#tracker')[0], this)) //if the new thing you focused on is not a descendant of #tracker
hideTracker();
e.stopPropagation();
});
Eh? So yeah, that's a certified hack. But it's a tough problem, and that's the best I can come up with at this hour.
Thank you all for your answers. Utilising the .focus() event rather than .blur() was a clever way to look at it. Unfortunately, it does raise a couple of browser problems, and I couldn't get any of the above working very robustly.
In the end I decided to use setTimeout(hideTracker, 100); to allow the focus() event to take place before the count of focussed elements within tracker was evaluated. Not ideal, but it's working well and the delay is fairly imperceptible.
Thanks again.

Categories