Chrome: 61.0.3163.73javascript
Safari: 10.0(IOS 10.3.3)css
Github: webapp-bugshtml
滑动到最顶部(最底部)的时候,停下,而后继续向上滑动(向下滑动)java
scrollTop
(scrollFix)当快滑到上边界或者下边界的值时,手动设置scrollTop
与达到边界时相差一像素(上边界时:scrollTop = 1
, 下边界时:scrollTop = elem.scrollHeight - elem.offsetHeight - 1
),这样就不会触发出界的极限条件。node
具体实现:jquery
var ScrollFix = function(elem) { // Variables to track inputs var startY, startTopScroll; elem = elem || document.querySelector(elem); // If there is no element, then do nothing if(!elem) return; // Handle the start of interactions elem.addEventListener('touchstart', function(event){ startY = event.touches[0].pageY; startTopScroll = elem.scrollTop; if(startTopScroll <= 0) elem.scrollTop = 1; if(startTopScroll + elem.offsetHeight >= elem.scrollHeight) elem.scrollTop = elem.scrollHeight - elem.offsetHeight - 1; }, false); };
注:1. 这个方法只能部分防止,在某些时候仍是会触发出界。2. 有说在全局滚动下和局部滚动下会有差别,可是就我测试的状况来讲,差别并非特别大。多是版本过高的缘由,具体结论还待测试更多机型。android
static
变为fixed
(测试效果貌似更好).toolbar { -webkit-box-sizing: border-box; padding: 1em; background: #222; color: #fff; font-weight: bold; text-align: center; height: 50px; /* 添加fixed头部 */ position: fixed; top: 0; z-index: 1; width: 100%; }
看以下代码:ios
// html <input type="email" class="form-control" id="inputEmail3" placeholder="Email"> <button id="submitBtn" class="btn btn-default">Sign in</button> // script var inputEmail3 = document.querySelector('#inputEmail3'); var submitBtn = document.querySelector('#submitBtn'); // way1 setTimeout(() => { inputEmail3.focus(); }, 2000);
这种方式下:在IOS
上输入框聚焦确没有办法弹出键盘git
爬墙爬到这么一个issue,3楼eddiemonge
老哥说到了,在IOS
下除非用户手动触发了输入框的focus
事件,才会触发键盘,至于设置定时器也是无论用的;可是,手动点击一个按钮,在按钮的操做中再来执行focus
事件却是管用的。他还给出了一个http://jsbin.com/inunis/8/edit?html,js,outputgithub
// 这样是能够弹出键盘的 submitBtn.onclick = function(e) { e.preventDefault(); inputEmail3.focus(); }
我为何会关注这个问题:那是由于我**(这里省略一万个草泥马)也遇到了这个问题呀,容我细细说来。
我有一个登陆页面,在聚焦以后须要往上弹一下,android
上正常,而后IOS
上还同时引出了一个BUG
:输入框上去了,可是光标却在下面闪。怎么办呢?固然是靠想办法解决呀,后来我就想在输入框上贴一层蒙版,点击了以后消失,同时在点击操做中,等到动画结束以后再执行输入框的focus
,行不行呢?好期待。。。
html
代码是这样的:
// ... 这里省略若干 <div class="col-sm-10"> <input type="email" class="form-control" id="inputEmail3" placeholder="Email" /> <div class="input-overlay" id="overlay"></div> </div>
样式:
.input-overlay { position: absolute; top: 0; left: 0; width: 100%; height: 100%; background-color: rgba(255, 0, 0, 0.3); z-index: 2; }
脚本:
overlay.addEventListener('touchstart', function(e) { e.preventDefault(); testForm.style.transform = 'translate3d(0, 0, 0)'; overlay.style.display = 'none'; overlay.style.zIndex = -1; setTimeout(function() { inputEmail3.focus(); }, 200); });
正所谓想象是好样的,可是实际行使起来却不是那么使人满意。是的,毫无效果。后来我想,是否是能够模拟一个事件,再触发一次点击,而后代码是这样的:
function mockEvent(fn) { var createDiv = document.createElement('div'); createDiv.style.display = 'none'; document.body.appendChild(createDiv); // 未作兼容 var e = document.createEvent('MouseEvent'); e.initEvent('xx', true, true); createDiv.addEventListener('xx', function() { fn && fn(); createDiv.remove(); }); createDiv.dispatchEvent(e); } overlay.addEventListener('click', function(e) { // ... setTimeout(function() { mockEvent(function() { inputEmail3.focus(); }); }, 200); });
答案依然是:不行。而后我想,是否是setTimeout
的缘由,只要存在延迟的状况下就不行。结果我去这么试了一下,将以前的按钮直接点击方式改成200ms
以后执行focus
。
submitBtn.onclick = function(e) { e.preventDefault(); setTimeout(function() { inputEmail3.focus(); }, 200); }
果真,只要设置延迟就不起效果了。顿时忽然想到移动端点透事件貌似有个300ms
延迟执行。虽然点透事件在移动端会被处理掉,然而我只是想验证一下个人猜测。而后我又这么写:
// html <div class="col-sm-10"> <input type="email" class="form-control" id="inputEmail3" placeholder="Email" /> <div class="input-overlay" id="overlay"></div> <a class="input-link" href="javascript:;" id="link">link</a> </div>
在overlay
下面放一个link
,而后在overlay
上绑定touchstart
事件,在link
上绑定click
事件。这样在上层的遮罩去掉以后,就能够300ms
后执行下面的link
层中的事情,那么也算是用户真正地触发的点击行为,美滋滋。结果我在代码中加了这个东西:
overlay.addEventListener('touchstart', function(e) { // ... }); link.addEventListener('click', function() { link.style.display = 'none'; link.style.zIndex = -1; inputEmail3.focus(); });
尼玛呀,仍是不行,绝望了。
然而。。。
天生不死心,又去爬墙呀。输入inupt move while cursor to stay where it was
。下面讲解决方案。
我找到了这样的一个issue。在其中的描述是:他的内容中有一输入框,而后focus
,当滑动内容时,光标不跟随移动,而在此输入的时候,光标又会回到输入框中。状况应该和我相似。
robby says
I also have this problem.
It is apparently related to the use of css transforms.I have fixed it with this hack workaround that forces redrawing as you scroll to eliminte this issue:
CSS:input { text-shadow: rgba(0,0,0,0) 0px 0px 0px; } input.force-redraw { text-shadow: none; }JS:
myScroll = new iScroll('wrapper', { onScrollMove: function() { $('input').toggleClass('force-redraw'); } });
是的,有木有很激动。因而我这样写:
// css input { text-shadow: rgba(0,0,0,0) 0px 0px 0px; } input.force-redraw { text-shadow: none; } // javascript inputEmail3.addEventListener('focus', function() { testForm.style.transform = 'translate3d(0, 0, 0)'; setTimeout(() => { inputEmail3.className = 'form-control force-redraw'; }, 300); });
效果大致上实现了,可是仍然有瑕疵。就是必须设置延迟300ms
以上,否则,光标重绘不正常,并且光标有明显的移动过程。因此若是童鞋们若是发现有什么更好的办法,还望不吝赐教。
另外,若是一个页面中有输入框,聚焦以后,滑动过程当中在IOS
上可能会出现不流畅的问题,其实能够这么作:监测页面的touchmove
事件,若是当前页面存在着输入框被active
,那么直接让其blur
,保证滑动过程当中没有输入框被聚焦。(不过以个人测试状况来看,在chrome
和safari
上滑动的时候输入框再也不被激活,相似在PC
端滑动的时候采用了蒙版或者points-event: none;
的效果)
var thisFocus; var content = document.querySelector('#content'); var inputs = document.getElementsByTagName('input'); for (var i = 0; i < inputs.length; i++) { var input = inputs[i]; input.onfocus = focused; } function focused(e) { thisFocus = e.target; } content.addEventListener('touchmove', function() { if (thisFocus) { thisFocus.blur(); thisFocus = null; } });
页面中有fixed
头部,输入框,而且输入框靠下时,当输入框focus
的时候,会将整个页面上移,致使头部被顶出去。fixed position div freezes on page
缘由大体是:ios
自带的输入居中效果,而带有fixed
头部在页面被顶上去的同时没有从新计算位置,致使显示错误。那么能够具体分这几步来解决:
focus
的时候采用fixed
固定头部focus
时,采用绝对定位头部,同时使用window.pageYOffset
来计算滑动的距离,设置头部的top
值scroll
方法,动态设置头部top
值fixed
定位代码请往这里看:
var isFocused, focusedResizing, ticking = false; window.tryfix = function() { var inputs = document.getElementsByTagName('input'); for (var i = 0; i < inputs.length; i++) { input = inputs[i]; input.onfocus = focused; input.onblur = blured; } window.onscroll = onScroll; } function focused(event) { isFocused = true; scrolled(); } function blured(event) { isFocused = false; var headStyle = document.getElementById('header').style; var footStyle = document.getElementById('footer').style; if (focusedResizing) { focusedResizing = false; headStyle.position = 'fixed'; headStyle.top = 0; footStyle.display = 'block'; } } function onScroll() { if (!ticking) { requestAnimationFrame(scrolled); ticking = true; } } function scrolled() { var headStyle = document.getElementById('header').style; var foot = document.getElementById('footer'); var footStyle = foot.style; if (isFocused) { if (!focusedResizing) { focusedResizing = true; headStyle.position = 'absolute'; footStyle.display = 'none'; } headStyle.top = window.pageYOffset + 'px'; // window.innerHeight wrong //var footTop = window.pageYOffset + window.innerHeight - foot.offsetHeight; //footStyle.bottom = (document.body.offsetHeight - footTop) + 'px'; } ticking = false; } tryfix();
另外若是页面缩放,也会引发头部定位不正常。详情能够看这里,关于anroid
上fixed
的支持状况,能够看这里
当输入框比较靠下时,android
上弹出键盘,会将输入框遮住。
说明:测试了不少机型,发现如今的android
上的浏览器都貌似修复了这个问题,就是当键盘弹上来的时候,会默认地将输入框上移。可是我在项目中内嵌的webview
中确实遇到了这种问题。
测试说明:测试的机型包括了如今一些主要的浏览器:chrome
、UC
、QQ
、Opera
、360
、百度、猎豹,测试的android
版本包括4.一、4.四、5.1等,测试的浏览器版本都有下载最低的历史版原本测。可是就测试的状况来看,除了猎豹浏览器会出现上述的状况以外,其余的基本表现正常。(更多测试量没作,没有这么多机器呀。尴尬😓)
逗比时刻:我为了测试较老的Android
版本,特意装了genymotion
,后来发现根本就没有键盘弹出。
总之,若是遇到了上述的问题,不妨能够试试这样的办法。
弹出键盘的时候,计算可视区域的高度以及输入框距离视口的高度加上自己的高度(可视区域、自身距离视口高度 + 自身高度)。若是可视区域的高度大于后者,说明此时的输入框须要上移,那么就将body
向上平移,不然不平移。在键盘消失的时候回归到原来的位置就好。具体能够看以下代码,另外能够看这个例子:https://jsbin.com/ganiqus
var availHeight = Math.max(document.documentElement.clientHeight || document.body.clientHeight); var inputs = document.getElementsByTagName('input'); var textareas = document.getElementsByTagName('textarea'); var footer = document.querySelector('#footer'); var ftStyle = footer.style; var body = document.body; var keyboardHeight; // 绑定事件 for (var i = 0; i < inputs.length; i++) { var input = inputs[i]; input.onfocus = focused; input.onblur = blured; } for (var i = 0; i < textareas.length; i++) { var textarea = textareas[i]; textarea.onfocus = focused; textarea.onblur = blured; } // 模拟事件 function mockEvent(type, fn) { var createDiv = document.createElement('div'); createDiv.style.display = 'none'; document.body.appendChild(createDiv); var e = document.createEvent('MouseEvent'); e.initEvent(type, true, true); createDiv.addEventListener(type, function() { fn && fn(); createDiv.remove(); }); createDiv.dispatchEvent(e); } function focused() { // 事件模拟 mockEvent('native.keyboardshow'); } function blured() { mockEvent('native.keyboardhide'); } function getOffsetTop(el) { var mOffsetTop = el.offsetTop; var mOffsetParent = el.offsetParent; while(mOffsetParent) { mOffsetTop += mOffsetParent.offsetTop + mOffsetParent.clientTop; mOffsetParent = mOffsetParent.offsetParent; } return mOffsetTop; } // 是否须要上移输入框 function needPullUpScreen(target, top, height) { var keyboardShow = false; var nodeName = target.nodeName; var leftHeight; var isAndroid = true; if (isAndroid) { leftHeight = availHeight - keyboardHeight; } else { leftHeight = availHeight - keyboardHeight * window.devicePixelRatio; } if (nodeName) { if ((top + height + 5) >= leftHeight) { switch (nodeName.toLowerCase()) { case 'input': keyboardShow = target.type !== 'button' && target.type !== 'file'; break; case 'textarea': keyboardShow = true; break; default: keyboardShow = false; break; } } } return keyboardShow; }; // 监听键盘弹出事件 window.addEventListener('native.keyboardshow', function(e) { ftStyle.display = 'none'; // 此处获取keyboard的高度,由插件提供,这里写死 // keyboardHeight = e.keyboardHeight; keyboardHeight = 290; var activeEle = document.activeElement; // getBoundingClientRect 只在android 4.4以上才有用 // top和height可用getOffsetTop(el)和el.offsetHeight替代 var boundingClientRect = activeEle.getBoundingClientRect(); var top = boundingClientRect.top; var height = boundingClientRect.height; // 移到居中位置 // 这个高度能够根据本身的状况来写 var moveOffset = top + height - availHeight / 2; if (activeEle && needPullUpScreen(activeEle, top, height)) { body.style.webkitTransform = `translate3d(0, ${-moveOffset}px, 0)`; body.style.transform = `translate3d(0, ${-moveOffset}px, 0)`; body.style.webkitTransition = 'transform 200ms'; body.style.transition = 'transform 200ms'; } }); // 监听键盘消失事件 window.addEventListener('native.keyboardhide', function() { body.style.webkitTransform = ''; body.style.transform = ''; body.style.webkitTransition = 'transform 200ms'; body.style.transition = 'transform 200ms'; setTimeout(function() { ftStyle.display = ''; }, 200); });
注意:
APP
的开发中,应该是有相关插件来监听键盘事件的,同时能够获取键盘的高度getBoundingClientRect
方法,能够用代码中提供的getOffsetTop
方法来替代IOS
中也遇到这样的问题,此时的键盘高度要乘以设备像素比