It's easy on any desktop browser. I just listen for keydown of document or window, and call preventDefault, as with the answers here and here.
I usually use something like this, so it's like ignoring all keyboard inputs:
window.addEventListener('keydown', function(e){
e.preventDefault();
e.stopPropagation();
e.stopImmediatePropagation();
}, true);
But they don't work on my Android tablet. (Tested on Chrome 63.0.3239.83 and Firefox 57.0.1 for Android)
Here is the typical scenario: I have an <input> focused as the current active element, and then I need some JavaScript to execute to freeze the user interface so all user inputs are ignored.
I wasted a lot of time on this issue and still cannot find a workaround for my Android tablet.
I considered some other ways, like changing focus to another element, or setting readOnly of the element to true. But there are caveats. Calling focus() triggers the onblur of the previously focused element. Setting readOnly does not prevent the event listeners of the focused element from firing (e.g. any keydown or keyup handlers for that <input>), and sometimes the enter key becomes a key that allows user to jump to the next input field on the page.
Is there a way to ignore all keyboard inputs on Chrome for Android without undesirable side effects?
At last I called window.addEventListener to attach the same handler (the function used in the question) for not only keydown, but also blur and focus, and then called document.documentElement.focus(). (Need to add tabindex="0" to <html>)
This avoids the problem of triggering onblur of the previously focused element, which is mentioned in the question.
(Note that blur and focus are not cancelable events, so handlers are only for stopping propagation here.)
Related
I want focus to stay on the currently focused DOM element. I am using the usual way to prevent focus change on click on another element:
button.addEventListener("mousedown", (evt)=>{evt.preventDefault();});
This works on normal clicks, but not on long press.
I expected it to work on long press too. Anyone knows if the behavior is a bug?
Is there any way to prevent focus change on long press?
Listen to the touchstart event:
element.addEventListener("touchstart", (evt)=>{evt.preventDefault();});
Note that you will still need the mousedown event listener for non-touch devices.
I use AngularJS 1.4.7
I have editable input. If we focus on input and after click outside of this input field, script will do function "save()" in ng-blur. Everything work correctly, but on iPhone 5/6 and iPad don't work (not execute anything in ng-blur). I don't know why, but I deduce that problem is in focus/touch action. Someone know where is the problem?
Fix ng-blur not working on empty space and most elements with iOS
Goal
In my app, we wanted to hide open menus or search results when clicking away from
the menu or search box.
Problem
iOS Safari does not blur properly when clicking away from the current element.
Note
You do not need a special directive. The problem is not with ng-blur, which works fine. The problem is that a blur event never occurs because of Apple's unusual design on iOS, regardless of whether you use ng-blur or a native DOM blur event.
Reason for Issue
Apple has designed mobile Safari to perform event bubbling differently than desktop browsers do. According to official Apple docs, if an element that is clicked has no click listener attached to it, then no click event is triggered. Without a click event, focus does not change and no blur event occurs, so the current element does not lose focus even though the user really did click elsewhere on the page.
The blur will only happen if the user clicked an element with a click event listener. Some elements like hyperlinks and inputs have "built-in" event listeners for click, and so will always register a click, causing blur to work.
Normally in desktop browsers, the click event is blindly fired whether there is a registered listener or not in the element's DOM hierarchy. That is why ng-blur works as expected in desktop browsers, even on "empty space".
Apple says that for iOS Safari they only dispatch the event if they find an element registered to listen for it.
Apple Docs - go to Figure 6-4
Apple's documentation makes it sound like they are looking for a registered event listener and handled on the target element, but that is not the case. I found the answers in the following article, and more importantly, one of the comments to it.
Quirksmode Article on iOS event delegation
First, observe that placing a listener on the document or body does not cause the click event to be dispatched on iOS Safari, if the target element has no click event listener and handler. It works in most browsers, but not on iOS.
It appears that Apple is checking the DOM hierarchy up to the <body> tag before dispatching the click event, but is not checking the <body> tag or above. Therefore, you need a click event listener attached to an element somewhere below the <body>.
Solution/Workaround
The solution then is very simple. All you need to do is to wrap all of the page content in a "master" container element just below the <body> element, and put the listener there instead of on the body or the document. It can even be an empty handler function. Apple is only checking to see if one is registered. The reason for wrapping all content is so that no matter where in the page the user clicks, the bubble-up process will eventually reach the master container.
If you do that, then ng-blur will work as expected in iOS, because the click event on empty space (an element with no click event listener) will pass the iOS check when it finds the parent container's click event listener and a click event will be dispatched normally, like it would be in any other browser.
Caution
This solution effectively causes iOS Safari to see a click event listener on every DOM element's hierarchy, tricking it into dispatching the click event on every element, just like a normal browser. I do not know if Apple has a performance reason they avoid this in iOS, or if it is just an aesthetic/developer type preference (see example). You will be changing the default iOS behavior in your app by using this workaround.
For example, iOS users may accidentally select text on your page that they did not mean too, and which would not normally occur without a click and hold gesture.
I don't know why that problem occurs.
but do one thing. create one small directive 'fake-blur' . then call your method in that directive
App.directive('fakeBlur', function(){
return {
restrict: 'A',
link: function(scope, element, attrs) {
element.blur(function(){
// call you function ex:
scope.save();
})
}
}
});
<input type="text" fake-blur/>
This is for mobile web apps, not native.
I have an autocomplete drop-down that closes on the blur event. But I want to prevent this from happening when the user closes the keyboard on mobile (ie the autocomplete dropdown should stay visible). Is there a way to distinguish a blur event caused by the keyboard closing, and other kinds of blur events? Can I prevent a blur event specifically caused by closing the keyboard in mobile?
Ok, first I would recommend checking your libraries' documentation because they might provide something in their API, although there's nothing I'm aware of. That's the disadvantage of Web Apps: you can't access native functionality.
If you still really want to do it, here's a possible solution. It's ugly, but it might work.
Container on tap function (event)
If !autocomplete return // if you cant see the popup do nothing and blur normally
If (event.target != inputID) AND (event.target != autocID)
CloseAutocomplete()`
Essentially, instead of closing the autocomplete on blur, close it whenever the user taps on your parent container, but not on the input itself or the autocomplete. Depending on how it works, you could extend it to check any tap on screen.
I see you tagged iOS... you can observe keyboard events with UIKeyboardWillHideNotification. [https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIWindow_Class/index.html#//apple_ref/c/data/UIKeyboardWillHideNotification] You may have to do and asynchronous delay to catch that event on with onblur, though.
If you really want to prevent all "other kinds of blur events" you could make your drop-down close on specific events (instead of on blur). For example: when a another field is clicked, navigation, and/or add an 'x' close button next to that drop-down control.
In the blur event listener, test for event.relatedTarget === null. If focus has moved to a different element, event.relatedTarget will refer to that element, but if there is no focus (as is the case when the cause of the blur is keyboard close), event.relatedTarget will be null.
I have a page with an input field. I want to run some javascript code on blur. Like this,
$('#inputfield').on('blur', function() {
...
});
It works fine on a desktop browser, if I tab away or click the mouse outside the field. On an iPad Safari browser, it works fine if I tap outside the input field.
But if I click on the hide keyboard button on the lower left corner, blur event is not triggered. Doesn't blur event fire on keyboard hide? I see the pointer/cursor moves away from the input field on keyboard hide. Is there any way to capture the keyboard hide event?
Thanks.
From Inforbiro:
jQuery - Focusout() and Blur() Event Differences
You can find many examples on Internet where you can see that focusout and blur events are used as synonyms and you can be confused with their usage.
What is difference between jQuery's .focusout() and .blur() events?
The difference between .focusout() and blur() jQuery event is that the .focusout() is sent to an element when it, or any element inside of it, loses focus. The .blur() event supports detecting the loss of focus from parent elements (i.e., it supports event bubbling)
What is event bubbling
Event bubbling in jQuery represents propagating event fired in one element to upper or lower elements in DOM hierarchy.
I've had problems in the past with blur, now I just stick to focusout
Take a look at the jQuery Documentation, they show an example of focusout vs blur
I need to determine what caused a focus event.
Ideally, I want to differentiate between a click, a tab/keyboard input, and a manual (via code) trigger.
How can I do this?
I'm looking at the event object, but I'm not seeing anything too useful.
If the focus comes from a $x.focus() call, then the event won't have an originalEvent property because there was no event from the browser so:
if(ev.hasOwnProperty('originalEvent')) {
// Focus event was manually triggered.
}
To differentiate between keyboard and mouse based focus events, you could try binding a keydown handler to everything else to detect a Tab or Shift-Tab but that would be a gross hack and probably not reliable; for example, on an iPad, you don't hit Tab to move to the next field, you hit Next or Previous in the popup keyboard to move around and those may not register as key presses at all.
There's a similar question about click events that might be of interest as well:
In jQuery, how can I tell between a programmatic and user click?
As you note in the comments, you could trap click events to detect a mouse-based focus change and set a flag somewhere to remember it. Then you'd have this:
If there is no originalEvent in the jQuery event then the focus change was triggered manually (i.e. $x.focus() or similar).
If the click handler flag is set then the focus change came from a mouse action.
Otherwise the focus change came from a keyboard event.
You'd have to be careful that your click and focus events came in the right order and you'd need to make sure the flag was cleared when you're done with it. This might not be bullet proof but maybe it doesn't need to be.