我对这个问题有一个不太优雅的解决方法,我希望其他人可能已经拥有更强大的解决方案。
在触摸屏上,点击可编辑的文本字段将显示屏幕键盘,这将更改可用的屏幕空间量。如果不加以处理,可能会隐藏关键元素,或者将页脚推出位置。
在笔记本电脑或台式计算机上,打开可编辑的文本字段不会创建此类布局更改。
在我当前的项目中,我想确保即使虚拟键盘打开也能看到某些关键项,因此我需要检测何时发生这种更改。然后我可以在body
元素中添加一个类,以更改布局以适应键盘的存在。
在线搜索现有解决方案时,我发现:
contentEditable
元素将打开屏幕键盘我已经发布了我在下面提出的解决方案。它依赖于在键盘焦点变化的第二个内检测窗口高度的变化。我希望你可以有一个更好的解决方案来提出跨平台,跨浏览器和跨设备的测试。
我创建了一个repository on GitHub 您可以测试我的解决方案here。
在我的测试中,如果用户使用带触摸屏和键盘鼠标的计算机,并且首先使用鼠标(取消)选择可编辑元素,然后立即更改窗口高度,则可能会出现误报。如果您在计算机或移动设备上发现其他误报或否定,请告诉我。
;(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 :(得分:0)
由于无法直接检测键盘开口,因此只能通过高度和宽度进行检测。 See more
在javascript中screen.availHeight
和screen.availWidth
可能有帮助。
答案 1 :(得分: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,所以它更快更顺畅!
注意:我发现使用'visibility:hidden'比'display:none'或'position:static'更少,但你的里程可能会有所不同。
答案 2 :(得分:0)
有一个新的实验性API,该API可以准确地跟踪由于键盘的出现和类似的其他移动异常导致的大小变化。
window.visualViewport
https://developer.mozilla.org/en-US/docs/Web/API/Visual_Viewport_API
通过收听调整大小的事件,并将高度与所谓的“布局视口”的高度进行比较。看到它发生了很大的变化,例如30像素。您可能会推断出“键盘正在显示”之类的内容。
if('visualViewport' in window) {
window.visualViewport.addEventListener('resize', function(event) {
if(event.target.height + 30 < document.scrollElement.clientHeight) {
console.log("keyboard up?");
} else {
console.log("keyboard down?");
}
});
}
(上面的代码未经测试,我怀疑缩放可能会触发误报,还可能需要检查缩放比例更改)