Mottie / Keyboard

Virtual Keyboard using jQuery ~
http://mottie.github.io/Keyboard/
Other
1.78k stars 723 forks source link

Preventing Native Virtual Keyboard from opening #334

Open mreis1 opened 9 years ago

mreis1 commented 9 years ago

I'm facing an issue with the virtual keyboard on mobile devices. Basically the native keyboard is showing up and being hidden immediately what causes a flickering in the UI, and bad ux.

I've tried to prevent the click event from propagating and also defined the lockInput property to ensure that the user only uses the virtual keyboard offered by the plugin. I've also tried to prevent the blur effect but the result is the same...

Does anyone have any thoughts on this?

Here's an example of the handler that reveals the keyboard

                   function revealKeyboard(e){
                        e.preventDefault();
                        e.stopPropagation();

                        var keyboard = input.data('keyboard');
                            keyboard.reveal();
                    }

here are the settings

var keyboardSettings = {
                            openOn : null,
                            layout : 'qwerty',
                            css: {
                                // input & preview
                                input: 'keyboard-input',
                                // keyboard container
                                container: 'keyboard-container', // jumbotron
                                // default state
                                buttonDefault: 'keyboard-btn keyboard-btn-default',
                                // hovered button
                                buttonHover: 'keyboard-btn-hover',
                                // Action keys (e.g. Accept, Cancel, Tab, etc);
                                // this replaces "actionClass" option
                                buttonAction: 'active',
                                // used when disabling the decimal button {dec}
                                // when a decimal exists in the input area
                                buttonDisabled: 'disabled'
                            },
                            lockInput: true
                        };

and here is how i initialize the field. In this case it's suppose to be the email field:

email = element.find('.email');
                        email.on('focus.' + scope.$id, function(e){
                            e.preventDefault();
                            e.stopPropagation();
                        });

                        email.keyboard(keyboardSettings);
                        email.click(function(){
                            email
                                .getkeyboard()
                                .reveal();
                        });
<div class="input">
        <input type="text" class="email" ng-model="userdata.id" required placeholder="{{ 'TYPE_EMAIL_OR_ID'| translate }}">
      </div>
Mottie commented 9 years ago

Hi @mreis1!

Sadly, I haven't found an ideal solution for hiding the native keyboard. I don't think it's possible to completely disable it with plain javascript.

I too would be happy to hear of an alternative that doesn't require loading in an app to turn off the keyboard.

mreis1 commented 9 years ago

Hi @Mottie, I'vent tried it yet but some guys at stackoverflow mentioned that triggering the blur event right after the focus will prevent the keyboard from showing.

here's my question: You will find a related question attached to the post: http://stackoverflow.com/questions/10940287/html-mobile-forcing-the-soft-keyboard-to-hide

Mottie commented 9 years ago

The problem is that the keyboard returns focus to that input to maintain the caret position (so you can see where you are typing & be able to insert text in the middle, etc).

The only way I've found to prevent the native keyboard from opening is to make the input readonly.

membla commented 9 years ago

Using the readonly attribute works well in my use case but gives the unexpected behaviour that a hidden clone of the input field with the same name and id is created, which is problematic as I'm integrating the keyboard in a form. Why is this? Also, FWIW, I don't get the backwards input when using the readonly attribute as stated in the docs, I do however get it when using the disabled attribute.

Mottie commented 9 years ago

Hi @membla!

That is odd, the preview input (if usePreview option is true) should not duplicate the ID of the original input. In fact it completely removes the ID attribute:

base.$preview = base.$el.clone(false)
    .removeAttr('id')

That is the only clone that is produced. Are you using the most up-to-date version?

I think the backwards input issue only occurs in some browsers, or it may not be an issue any more. Disabled inputs do not return a caret position and therefore should not have a keyboard applied to it - the plugin only checks for disabled keyboard on initialization.

Mottie commented 9 years ago

Oh wait, I stand corrected... there is a clone of the preview (which is the actual element when usePreview is false... I'll make sure that doesn't include an id as well.

membla commented 9 years ago

Thanks for the prompt response @Mottie! I haven't studied your source but why is there a reason for the input clone to be used at all when usePreview is false?

From my testing I could not reproduce the backwards input when using the readonly attribute, only when using the disabled attribute, and then only in IE... This is on input elements, did not do any testing on textarea.

Mottie commented 9 years ago

The reason for the clone is to get an accurate scrollLeft and scrollTop position for the current caret position. Otherwise the user could be using the virtual keyboard to add content that isn't visible. Sadly, this still appears to be a problem for IE (all versions; see #313).

mreis1 commented 9 years ago

Hi guys, just to put you occurrent I managed to block the virtual native keyboard from mobile devices by destroying the keyboard when the keyboard is closed and recreating it each time we need it

//here's the click event on the input field
e.preventDefault();
                            e.stopPropagation();
                            keyboardOptions = $.extend({},keyboardDefaultSettings, getKeyboardSettings());
                            var el = $(this),
                            keyboard = (el.getKeyboard) ? el.getKeyboard() : false;

                            if (!keyboard) {
                                el.keyboard (keyboardOptions);
                                keyboard = el.getkeyboard()
                            }

                            keyboard.reveal();
                            return ;
Mottie commented 9 years ago

Hi @mreis1!

I don't see where the keyboard is being destroyed. Also, it would be nice to know what keyboard options are being used.

I wonder if the solution is to preventDefault & stopPropagation before revealing the keyboard...

mreis1 commented 9 years ago

@Mottie I've tried many tricks but here is the explanation. At first I was creating the keyboard on each page render but because I was using a Single Page Application, transitioning between states in mobile was causing the native keyboard to popup and fadeout which was not the desired. Then i come up with the idea of creating the keyboard on click and keeping a reference for further use but when i tried to use the reference, the keyboard started to popping up.

The next move was quiet simple. Because I realize that clicking the input and creating the keyboard was working just fine, so I decided to destroy the keyboard on hide event and recreate it everytime the input was clicked.

Guess what... it worked like a charm at least in a Galaxy Tab Android 4.4.x

Here's the options used by default: notice the destroy at hidden property

var keyboardDefaultSettings = {
        openOn : null,
        layout : 'qwerty',
        css: {
            // input & preview
            input: 'keyboard-input',
            // keyboard container
            container: 'keyboard-container', // jumbotron
            // default state
            buttonDefault: 'keyboard-btn keyboard-btn-default',
            // hovered button
            buttonHover: 'keyboard-btn-hover',
            // Action keys (e.g. Accept, Cancel, Tab, etc);
            // this replaces "actionClass" option
            buttonAction: 'active',
            // used when disabling the decimal button {dec}
            // when a decimal exists in the input area
            buttonDisabled: 'disabled'
        },
        lockInput: true,
        hidden: function(event, keyboard, el){
            keyboard.destroy();
        }
    };
iElement.on(events.click, function(e){
                            e.preventDefault();
                            e.stopPropagation();
                            keyboardOptions = $.extend({},keyboardDefaultSettings, getKeyboardSettings());
                            var el = $(this),
                            keyboard = (el.getKeyboard) ? el.getKeyboard() : false;

                            if (!keyboard) {
                                el.keyboard (keyboardOptions);
                                keyboard = el.getkeyboard()
                            }

                            keyboard.reveal();
                            return ;
                        });
Handbuch commented 8 years ago

This did the job for me, worked on Android 4.2.2 tablet and iPhone (though I don't recommend this for iPhone, as the js-keyboard is very slow => usability)

$('input[type="text"]').keyboard({
        beforeVisible: function (e, keyboard, el) {
            keyboard.el.blur();
        }
});

I guess event.stopPropagation() is not working because the native on-screen keyboard is triggered by the OS / browser directly.

As mentioned by @Mottie, this defeats the caret, so the user can not see where he is inserting the text.

ioannisth commented 8 years ago

My solution for stopping the native keyboard to display is to set the Input tag as readonly, then wrap a div around it to which the clicks are captured and trigger the keyboard reveal function. Like this:

div class="vkeywrapper"> input readonly class="vkey" ... /></div

Then $('.vkeywrapper').click(function(e) { $(this).find('.vkey').getkeyboard().reveal();});

UPDATE: I forgot to mention, I have removed from jquery.keyboard.js the part that applies the *-nokeyboard class to readonly elements (this makes the keyboard not being displayed)

However, I am facing a problem , that the keyboard is very laggy and deosn't register the keypresses. The keys appear as being pressed visually but they need a long press in order to register. I don't want to reveal the site in public so @Mottie please feel free to contact me about this

Mottie commented 8 years ago

@Handbuch: there is a caret extension which adds a caret that can be styled. Check out the "QWERTY (mod) Text Area" example on the main demo page. Also, check out this demo which uses the caret extension on a password type input.

Hi @ioannisth!

Are you using the latest version of this plugin? That might solve the laggy problem. Also make sure that the mousewheel plugin is not included.

digeverything commented 6 years ago

I ended up adding readonly to the <input> tag, this will stop the native keyboard from appearing (tested on Android "Pixel 2"). Though remember to remove it for desktop users, which will require some device detection which is never fun.