Обнаружение открытия или закрытия виртуальной клавиатуры на устройстве с сенсорным экраном

1

У меня есть непродуманное решение этой проблемы, и я надеюсь, что у других уже могут быть более надежные решения.


На сенсорном экране нажатие на редактируемое текстовое поле приведет к появлению экранной клавиатуры, и это изменит объем доступного пространства экрана. Если вы не лечите, это может скрыть ключевые элементы или нажать нижний колонтитул на место.

На ноутбуке или настольном компьютере открытие редактируемого текстового поля не создает таких изменений макета.

В моем текущем проекте я хочу убедиться, что некоторые ключевые элементы видны, даже когда виртуальная клавиатура открыта, поэтому мне нужно определить, когда произойдет такое изменение. Затем я могу добавить класс в элемент body, чтобы изменить макет в соответствии с наличием клавиатуры.

При поиске существующих решений в Интернете я обнаружил, что:

  1. Не существует идеального способа узнать, что ваш код работает на мобильном устройстве
  2. Существуют немобильные устройства с сенсорными экранами, которые также могут иметь клавиатуры
  3. Элемент фокусировки может не редактироваться
  4. Элементы contentEditable откроют экранную клавиатуру
  5. В адресной строке может появиться повторное появление и занятие необходимого пространства экрана, в то время как появляется виртуальная клавиатура, еще больше сжимающая доступное пространство.

Я опубликовал решение, которое я привел ниже. Он основан на обнаружении изменения высоты окна в течение секунды изменения фокуса клавиатуры. Я надеюсь, что у вас может быть лучшее решение, предлагающее протестировать кросс-платформенный, кросс-браузер и все устройства.


Я создал репозиторий на GitHub.
Вы можете проверить мое решение здесь.

В моих тестах это может привести к ложному срабатыванию, если пользователь использует компьютер с сенсорным экраном и клавиатурой и мышью, а сначала использует мышь (de-), выбирает редактируемый элемент, а затем сразу же изменяет высоту окна. Если вы обнаружите другие ложные срабатывания или негативы, будь то на компьютере или мобильном устройстве, пожалуйста, дайте мне знать.


;(function (){

  class Keyboard {
    constructor () {
      this.screenWidth = screen.width        // detect orientation
      this.windowHeight = window.innerHeight // detect keyboard change
      this.listeners = {
        resize: []
      , keyboardchange: []
      , focuschange: []
      }

      this.isTouchScreen = 'ontouchstart' in document.documentElement

      this.focusElement = null
      this.changeFocusTime = new Date().getTime()
      this.focusDelay = 1000 // at least 600 ms is required

      let focuschange = this.focuschange.bind(this)
      document.addEventListener("focus", focuschange, true)
      document.addEventListener("blur", focuschange, true)

      window.onresize = this.resizeWindow.bind(this)
    }

    focuschange(event) {
      let target = event.target
      let elementType = null
      let checkType = false
      let checkEnabled = false
      let checkEditable = true

      if (event.type === "focus") {
        elementType = target.nodeName
        this.focusElement = target

        switch (elementType) {
          case "INPUT":
            checkType = true
          case "TEXTAREA":
            checkEditable = false
            checkEnabled = true
          break
        }

        if (checkType) {
          let type = target.type
          switch (type) {
            case "color":
            case "checkbox":
            case "radio":
            case "date":
            case "file":
            case "month":
            case "time":
              this.focusElement = null
              checkEnabled = false
            default:
              elementType += "[type=" + type +"]"
          }
        }

        if (checkEnabled) {
          if (target.disabled) {
            elementType += " (disabled)"
            this.focusElement = null
          }
        }

        if (checkEditable) {
          if (!target.contentEditable) {
            elementType = null
            this.focusElement = null
          }
        }
      } else {
        this.focusElement = null
      }

      this.changeFocusTime = new Date().getTime()

      this.listeners.focuschange.forEach(listener => {
        listener(this.focusElement, elementType)
      })
    }

    resizeWindow() {
      let screenWidth = screen.width;
      let windowHeight = window.innerHeight
      let dimensions = {
        width: innerWidth
      , height: windowHeight
      }
      let orientation = (screenWidth > screen.height)
                      ? "landscape"
                      : "portrait"

      let focusAge = new Date().getTime() - this.changeFocusTime
      let closed = !this.focusElement
                && (focusAge < this.focusDelay)            
                && (this.windowHeight < windowHeight)
      let opened = this.focusElement 
                && (focusAge < this.focusDelay)
                && (this.windowHeight > windowHeight)

      if ((this.screenWidth === screenWidth) && this.isTouchScreen) {
        // No change of orientation

        // opened or closed can only be true if height has changed.
        // 
        // Edge case
        // * Will give a false positive for keyboard change.
        // * The user has a tablet computer with both screen and
        //   keyboard, and has just clicked into or out of an
        //   editable area, and also changed the window height in
        //   the appropriate direction, all with the mouse.

        if (opened) {
          this.keyboardchange("shown", dimensions)
        } else if (closed) {
          this.keyboardchange("hidden", dimensions)
        } else {
          // Assume this is a desktop touchscreen computer with
          // resizable windows
          this.resize(dimensions, orientation)
        }

      } else {
        // Orientation has changed
        this.resize(dimensions, orientation)
      }

      this.windowHeight = windowHeight
      this.screenWidth = screenWidth
    }

    keyboardchange(change, dimensions) {
      this.listeners.keyboardchange.forEach(listener => {
        listener(change, dimensions)
      })
    }

    resize(dimensions, orientation) {
      this.listeners.resize.forEach(listener => {
        listener(dimensions, orientation)
      })
    }

    addEventListener(eventName, listener) {
      // log("*addEventListener " + eventName)

      let listeners = this.listeners[eventName] || []
      if (listeners.indexOf(listener) < 0) {
        listeners.push(listener)
      }
    }

    removeEventListener(eventName, listener) {
      let listeners = this.listeners[eventName] || []
      let index = listeners.indexOf(listener)

      if (index < 0) {
      } else {       
        listeners.slice(index, 1)
      }
    }
  }

  window.keyboard = new Keyboard()

})()
  • 0
    Вы можете использовать screen.availHeight и screen.availWidth чтобы обнаружить изменения размера экрана. Я тоже это нашел
Теги:
event-handling
keyboard

2 ответа

0

Это трудная проблема, чтобы получить "право". Вы можете попытаться скрыть нижний колонтитул на фокусе элемента ввода и показать размытие, но это не всегда надежно на iOS. Каждый раз так часто (один раз в десять, скажем, на моем iPhone 4S) фокусное событие, похоже, не срабатывает (или, может быть, есть состояние гонки с JQuery Mobile), а нижний колонтитул не скрывается.

После долгих проб и ошибок я придумал это интересное решение:

<head>
    ...various JS and CSS imports...
    <script type="text/javascript">
        document.write( '<style>#footer{visibility:hidden}@media(min-height:' + ($( window ).height() - 10) + 'px){#footer{visibility:visible}}</style>' );
    </script>
</head>

По существу: используйте JavaScript, чтобы определить высоту окна устройства, а затем динамически создайте мультимедийный запрос CSS, чтобы скрыть нижний колонтитул, когда высота окна уменьшается на 10 пикселей. Поскольку открытие клавиатуры изменяет размер экрана браузера, это никогда не прерывается в iOS. Поскольку он использует механизм CSS, а не JavaScript, он намного быстрее и плавнее!

Примечание. Я обнаружил, что "видимость: скрытая" менее глючная, чем "display: none" или "position: static", но ваш пробег может отличаться.

0

Поскольку нет прямого способа обнаружить открытие клавиатуры, вы можете определять только по высоте и ширине. Узнать больше

В javascript screen.availHeight и screen.availWidth может помочь.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню