This repository has been archived by the owner. It is now read-only.

Prevent keyboard from hiding #81

Closed
buunguyen opened this Issue Apr 14, 2015 · 14 comments

Comments

Projects
None yet
@buunguyen

buunguyen commented Apr 14, 2015

I'm building an app where I want the keyboard to stay open even after the input loses focus.

After reading posts on Ionic forum and StackOverflow, I tried to refocus on the text input after it loses focus. However, the input only gets focused in desktop browsers, not Safari iOS. It looks like iOS ignores calls to focus(), with or without wrapping in a timeout. This is what I tried:

.directive('constantFocus', function($timeout){
  return {
    restrict: 'A',
    link: function(scope, element, attrs) {
      element[0].addEventListener('focusout', function(e) {
        $timeout(function() {element[0].focus();})
      });
    }
  };
})
<textarea constant-focus></constant-focus>

So, there are really two questions:

  • Is there a way to make keyboard stay open without having to refocus programmatically?
  • If having to refocus, is there a way to make focus() work on Safari iOS?

Thanks!

@mlynch

This comment has been minimized.

Show comment
Hide comment
@mlynch

mlynch Apr 14, 2015

Member

Random idea: could try toggling autofocus on an input?

Member

mlynch commented Apr 14, 2015

Random idea: could try toggling autofocus on an input?

@buunguyen

This comment has been minimized.

Show comment
Hide comment
@buunguyen

buunguyen Apr 14, 2015

Thanks. I just tried it, but the input still doesn't receive focus. Is it necessary (or possible) to hack the keyboard plugin to make this work? I haven't read the source but thought I should check first before sinking time on it.

buunguyen commented Apr 14, 2015

Thanks. I just tried it, but the input still doesn't receive focus. Is it necessary (or possible) to hack the keyboard plugin to make this work? I haven't read the source but thought I should check first before sinking time on it.

@alvaromontoro

This comment has been minimized.

Show comment
Hide comment
@alvaromontoro

alvaromontoro Apr 14, 2015

I had a similar issue but in Android: the keyboard flashed because it got hidden when the input lost focus, and then reshown when the input regained focus.

After testing with different configurations, finally I found something that worked for me: triggering the click event, and then the focus event for the input element. I don't know if that will work on iOS, but it's worth a try. The code looks like this: $textbox.click().focus();

alvaromontoro commented Apr 14, 2015

I had a similar issue but in Android: the keyboard flashed because it got hidden when the input lost focus, and then reshown when the input regained focus.

After testing with different configurations, finally I found something that worked for me: triggering the click event, and then the focus event for the input element. I don't know if that will work on iOS, but it's worth a try. The code looks like this: $textbox.click().focus();

@tlancina

This comment has been minimized.

Show comment
Hide comment
@tlancina

tlancina Apr 14, 2015

Member

Make sure you have <preference name="KeyboardDisplayRequiresUserAction" value="false"/> in your config.xml if you want to call focus() on iOS.

https://cordova.apache.org/docs/en/4.0.0/guide_platforms_ios_config.md.html#iOS%20Configuration

Member

tlancina commented Apr 14, 2015

Make sure you have <preference name="KeyboardDisplayRequiresUserAction" value="false"/> in your config.xml if you want to call focus() on iOS.

https://cordova.apache.org/docs/en/4.0.0/guide_platforms_ios_config.md.html#iOS%20Configuration

@buunguyen

This comment has been minimized.

Show comment
Hide comment
@buunguyen

buunguyen Apr 14, 2015

@tlancina awesome, setting that pref to false work. Thank you!

buunguyen commented Apr 14, 2015

@tlancina awesome, setting that pref to false work. Thank you!

@buunguyen buunguyen closed this Apr 14, 2015

@edgebal

This comment has been minimized.

Show comment
Hide comment
@edgebal

edgebal Feb 7, 2017

As a post-mortem note (this issue's page keep appearing first on Google):

If you want to keep the keyboard open when user clicks a button (i.e on a "Send" or "Search" button), you should use (mousedown)="something(); $event.preventDefault()" on that button. This will prevent the input from losing focus and the keyboard closing.

This way was successfully tested on Ionic2.

edgebal commented Feb 7, 2017

As a post-mortem note (this issue's page keep appearing first on Google):

If you want to keep the keyboard open when user clicks a button (i.e on a "Send" or "Search" button), you should use (mousedown)="something(); $event.preventDefault()" on that button. This will prevent the input from losing focus and the keyboard closing.

This way was successfully tested on Ionic2.

@manishoswal

This comment has been minimized.

Show comment
Hide comment
@manishoswal

manishoswal Mar 30, 2017

@edgebal - The solution works on android. Do we need to do something else for ios?

manishoswal commented Mar 30, 2017

@edgebal - The solution works on android. Do we need to do something else for ios?

@msio

This comment has been minimized.

Show comment
Hide comment
@msio

msio Apr 14, 2017

@edgebal is it working with WKWebView too ?
thanks

msio commented Apr 14, 2017

@edgebal is it working with WKWebView too ?
thanks

@RohinMohanadas

This comment has been minimized.

Show comment
Hide comment
@RohinMohanadas

RohinMohanadas Jun 12, 2017

@edgebal Thank you so much :) @manishoswal @msio777 tested in iOS and seems to be working fine 👍

RohinMohanadas commented Jun 12, 2017

@edgebal Thank you so much :) @manishoswal @msio777 tested in iOS and seems to be working fine 👍

@larssn

This comment has been minimized.

Show comment
Hide comment
@larssn

larssn Jul 19, 2017

@edgebal It works, but it seems to break some underlying native functionality. On Android I'm seeing the textarea not being cleared correctly, and on iOS spelling suggestions become glitchy.

Edit: touchend is an alternative event, but it has the same problems I mentioned.

larssn commented Jul 19, 2017

@edgebal It works, but it seems to break some underlying native functionality. On Android I'm seeing the textarea not being cleared correctly, and on iOS spelling suggestions become glitchy.

Edit: touchend is an alternative event, but it has the same problems I mentioned.

@chrisjpalmer

This comment has been minimized.

Show comment
Hide comment
@chrisjpalmer

chrisjpalmer Jul 31, 2017

Button within a slides control
These are the lengths I had to go to in order to stop the keyboard from hiding.

export class CommentsComponent {

  @ViewChild('sendButton') sendButton:Button;

  constructor() {
    
  }

  ngAfterViewInit() {
    let el = this.sendButton._elementRef.nativeElement;
    el.addEventListener('click', (event) => {
      this.stopBubble(event);
    });
    el.addEventListener('mousedown', (event) => {
      this.stopBubble(event);
    });
    el.addEventListener('touchdown', (event) => {
      this.stopBubble(event);
    });
    el.addEventListener('touchmove', (event) => {
      this.stopBubble(event);
    });
    el.addEventListener('touchstart', (event) => {
      this.stopBubble(event);
    });
    el.addEventListener('touchend', (event) => { //Triggered by a phone
      this.stopBubble(event);
      this.sendPressed();
    });
    el.addEventListener('mouseup', (event) => { //Triggered by the browser
      this.sendPressed();
    });
  }

  stopBubble(event) {
    event.preventDefault(); 
    event.stopPropagation(); //Stops event bubbling
  }

  sendPressed() {
    //... send message routine
  }

}

Why??
If your button is embedded in a slides control, the swiper receives all click, mouse and touch, events. This is so that when you swipe the slides, it can automatically blur any focussed control to tidy up the view for the next transition. This works to your detriment however when you want to implement a send button and have the keyboard remain where it is.

The solution is to bind to the native events of the button and call event.stopPropagation(). This method prevents a phenomenon called 'bubbling'. Bubbling is where the click event of a child DOM element gets sent to its parent. By disabling bubbling with the event.stopPropagation() method, you can prevent the slides control from ever receiving the mouse down message.

I tried doing this with angular event bindings like normal but I was unsuccessful. I resorted to a more direct method of binding direct to the control.

Would be great to see if we can add a directive to buttons embedded in slides which can prevent the slide from responding to the events.

chrisjpalmer commented Jul 31, 2017

Button within a slides control
These are the lengths I had to go to in order to stop the keyboard from hiding.

export class CommentsComponent {

  @ViewChild('sendButton') sendButton:Button;

  constructor() {
    
  }

  ngAfterViewInit() {
    let el = this.sendButton._elementRef.nativeElement;
    el.addEventListener('click', (event) => {
      this.stopBubble(event);
    });
    el.addEventListener('mousedown', (event) => {
      this.stopBubble(event);
    });
    el.addEventListener('touchdown', (event) => {
      this.stopBubble(event);
    });
    el.addEventListener('touchmove', (event) => {
      this.stopBubble(event);
    });
    el.addEventListener('touchstart', (event) => {
      this.stopBubble(event);
    });
    el.addEventListener('touchend', (event) => { //Triggered by a phone
      this.stopBubble(event);
      this.sendPressed();
    });
    el.addEventListener('mouseup', (event) => { //Triggered by the browser
      this.sendPressed();
    });
  }

  stopBubble(event) {
    event.preventDefault(); 
    event.stopPropagation(); //Stops event bubbling
  }

  sendPressed() {
    //... send message routine
  }

}

Why??
If your button is embedded in a slides control, the swiper receives all click, mouse and touch, events. This is so that when you swipe the slides, it can automatically blur any focussed control to tidy up the view for the next transition. This works to your detriment however when you want to implement a send button and have the keyboard remain where it is.

The solution is to bind to the native events of the button and call event.stopPropagation(). This method prevents a phenomenon called 'bubbling'. Bubbling is where the click event of a child DOM element gets sent to its parent. By disabling bubbling with the event.stopPropagation() method, you can prevent the slides control from ever receiving the mouse down message.

I tried doing this with angular event bindings like normal but I was unsuccessful. I resorted to a more direct method of binding direct to the control.

Would be great to see if we can add a directive to buttons embedded in slides which can prevent the slide from responding to the events.

@numerized

This comment has been minimized.

Show comment
Hide comment
@numerized

numerized Oct 22, 2017

WOW @chrisjpalmer !!!! Thanks A LOT ! I'm using it in a chat and it works like a charm !
This should be a parameter of ionic-plugin-keyboard.

numerized commented Oct 22, 2017

WOW @chrisjpalmer !!!! Thanks A LOT ! I'm using it in a chat and it works like a charm !
This should be a parameter of ionic-plugin-keyboard.

@johnthackstonanderson

This comment has been minimized.

Show comment
Hide comment
@johnthackstonanderson

johnthackstonanderson Oct 24, 2017

Here is a directive I made for suppressing events on a button based on @chrisjpalmer

import {  Directive, ElementRef, EventEmitter, OnChanges, SimpleChanges } from '@angular/core';

@Directive({
	selector: '[suppressEvents]',
	inputs: ["suppressEvents"],
	outputs: ["onClick"]
})
export class SuppressEvents implements OnChanges {

	suppressEvents: string | string[];

	constructor(public element: ElementRef) {

	}

	ngOnChanges(changes: SimpleChanges) {
		if (changes.suppressEvents) {
			if (changes.suppressEvents.firstChange) {
				console.log(this.suppressEvents);
				let el = this.element.nativeElement;

				if (this.suppressEvents == "all" || this.suppressEvents == null) {
					this.suppressEvents = ["click", "mousedown", "touchdown", "touchmove", "touchstart"];

				} else if (typeof this.suppressEvents == "string") {
					this.suppressEvents = [this.suppressEvents];
				} else if (typeof this.suppressEvents == "object" && !Array.isArray(this.suppressEvents)) {
					let r: string[] = [];
					for (let e of this.suppressEvents) {
						r.push(e);
					}
					this.suppressEvents = r;
				}
				for (let evName of this.suppressEvents) {
					el.addEventListener(evName, (event) => {
						this.stopBubble(event);
					});
				}



				el.addEventListener('touchend', (event) => { //Triggered by a phone
					this.stopBubble(event);
					this.onClick.emit(event);
				});
				el.addEventListener('mouseup', (event) => { //Triggered by the browser
					this.onClick.emit(event);
				});
			}
		}
	}

	stopBubble(event) {
		event.preventDefault();
		event.stopPropagation(); //Stops event bubbling
	}

	onClick: any = new EventEmitter();
}

johnthackstonanderson commented Oct 24, 2017

Here is a directive I made for suppressing events on a button based on @chrisjpalmer

import {  Directive, ElementRef, EventEmitter, OnChanges, SimpleChanges } from '@angular/core';

@Directive({
	selector: '[suppressEvents]',
	inputs: ["suppressEvents"],
	outputs: ["onClick"]
})
export class SuppressEvents implements OnChanges {

	suppressEvents: string | string[];

	constructor(public element: ElementRef) {

	}

	ngOnChanges(changes: SimpleChanges) {
		if (changes.suppressEvents) {
			if (changes.suppressEvents.firstChange) {
				console.log(this.suppressEvents);
				let el = this.element.nativeElement;

				if (this.suppressEvents == "all" || this.suppressEvents == null) {
					this.suppressEvents = ["click", "mousedown", "touchdown", "touchmove", "touchstart"];

				} else if (typeof this.suppressEvents == "string") {
					this.suppressEvents = [this.suppressEvents];
				} else if (typeof this.suppressEvents == "object" && !Array.isArray(this.suppressEvents)) {
					let r: string[] = [];
					for (let e of this.suppressEvents) {
						r.push(e);
					}
					this.suppressEvents = r;
				}
				for (let evName of this.suppressEvents) {
					el.addEventListener(evName, (event) => {
						this.stopBubble(event);
					});
				}



				el.addEventListener('touchend', (event) => { //Triggered by a phone
					this.stopBubble(event);
					this.onClick.emit(event);
				});
				el.addEventListener('mouseup', (event) => { //Triggered by the browser
					this.onClick.emit(event);
				});
			}
		}
	}

	stopBubble(event) {
		event.preventDefault();
		event.stopPropagation(); //Stops event bubbling
	}

	onClick: any = new EventEmitter();
}
@chrisjpalmer

This comment has been minimized.

Show comment
Hide comment
@chrisjpalmer

chrisjpalmer Oct 24, 2017

Hi @johnthackstonanderson thanks so much. Thats a great innovation. I will give this a try a bit later.

chrisjpalmer commented Oct 24, 2017

Hi @johnthackstonanderson thanks so much. Thats a great innovation. I will give this a try a bit later.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.