ios8中的Safari是固定的元素获得焦点时的滚动屏幕
在IOS8 Safari中有一个固定位置的新bug。
如果您将焦点放在固定面板中的textarea,safari会将您滚动到页面的底部。
这使得各种用户界面无法使用,因为你没有办法input文本到textareas没有滚动页面一路下降,失去你的地方。
有没有办法彻底解决这个bug?
#a { height: 10000px; background: linear-gradient(red, blue); } #b { position: fixed; bottom: 20px; left: 10%; width: 100%; height: 300px; } textarea { width: 80%; height: 300px; }
<html> <body> <div id="a"></div> <div id="b"><textarea></textarea></div> </body> </html>
基于这个问题的良好分析 ,我已经在CSS和html
元素中使用了这个:
html,body{ -webkit-overflow-scrolling : touch !important; overflow: auto !important; height: 100% !important; }
我认为这对我很好。
我能想出的最好的解决scheme是切换到使用position: absolute;
重点和计算它在使用position: fixed;
时的position: fixed;
。 关键是focus
事件太晚了,所以必须使用touchstart
。
这个答案的解决scheme非常接近地模仿我们在iOS 7中的正确行为。
要求:
当元件切换到绝对位置时, body
元件必须具有定位以确保适当的定位。
body { position: relative; }
代码 ( 实例 ):
以下代码是提供的testing用例的基本示例,可以根据您的具体使用情况进行调整。
//Get the fixed element, and the input element it contains. var fixed_el = document.getElementById('b'); var input_el = document.querySelector('textarea'); //Listen for touchstart, focus will fire too late. input_el.addEventListener('touchstart', function() { //If using a non-px value, you will have to get clever, or just use 0 and live with the temporary jump. var bottom = parseFloat(window.getComputedStyle(fixed_el).bottom); //Switch to position absolute. fixed_el.style.position = 'absolute'; fixed_el.style.bottom = (document.height - (window.scrollY + window.innerHeight) + bottom) + 'px'; //Switch back when focus is lost. function blured() { fixed_el.style.position = ''; fixed_el.style.bottom = ''; input_el.removeEventListener('blur', blured); } input_el.addEventListener('blur', blured); });
这里是相同的代码没有黑客比较 。
警告:
如果position: fixed;
元素除了body
之外还有其他任何具有定位的父元素,切换到position: absolute;
可能有意想不到的行为 由于position: fixed;
的性质position: fixed;
这可能不是一个大问题,因为嵌套这样的元素并不常见。
build议:
尽pipetouchstart
事件的使用会过滤掉大部分的桌面环境,但是您可能需要使用用户代理嗅探,以便此代码只能运行在破损的iOS 8上,而不能运行在其他设备(如Android和较旧的iOS版本)上。 不幸的是,我们还不知道苹果什么时候会在iOS上解决这个问题,但是如果在下一个主要版本中没有解决,我会感到惊讶。
我发现了一种不需要改变绝对位置的方法!
完整的未注释的代码
var scrollPos = $(document).scrollTop(); $(window).scroll(function(){ scrollPos = $(document).scrollTop(); }); var savedScrollPos = scrollPos; function is_iOS() { var iDevices = [ 'iPad Simulator', 'iPhone Simulator', 'iPod Simulator', 'iPad', 'iPhone', 'iPod' ]; while (iDevices.length) { if (navigator.platform === iDevices.pop()){ return true; } } return false; } $('input[type=text]').on('touchstart', function(){ if (is_iOS()){ savedScrollPos = scrollPos; $('body').css({ position: 'relative', top: -scrollPos }); $('html').css('overflow','hidden'); } }) .blur(function(){ if (is_iOS()){ $('body, html').removeAttr('style'); $(document).scrollTop(savedScrollPos); } });
打破了
首先,你需要在HTML页面的顶部有一个固定的input字段(这是一个固定的元素,所以它应该在语义上有意义,让它靠近顶部)
<!DOCTYPE HTML> <html> <head> <title>Untitled</title> </head> <body> <form class="fixed-element"> <input class="thing-causing-the-issue" type="text" /> </form> <div class="everything-else">(content)</div> </body> </html>
那么你需要将当前的滚动位置保存到全局variables中:
//Always know the current scroll position var scrollPos = $(document).scrollTop(); $(window).scroll(function(){ scrollPos = $(document).scrollTop(); }); //need to be able to save current scroll pos while keeping actual scroll pos up to date var savedScrollPos = scrollPos;
那么你需要一种方法来检测iOS设备,所以它不会影响不需要修复的东西(函数取自https://stackoverflow.com/a/9039885/1611058 )
//function for testing if it is an iOS device function is_iOS() { var iDevices = [ 'iPad Simulator', 'iPhone Simulator', 'iPod Simulator', 'iPad', 'iPhone', 'iPod' ]; while (iDevices.length) { if (navigator.platform === iDevices.pop()){ return true; } } return false; }
现在,我们有我们需要的一切,这里是修复:)
//when user touches the input $('input[type=text]').on('touchstart', function(){ //only fire code if it's an iOS device if (is_iOS()){ //set savedScrollPos to the current scroll position savedScrollPos = scrollPos; //shift the body up a number of pixels equal to the current scroll position $('body').css({ position: 'relative', top: -scrollPos }); //Hide all content outside of the top of the visible area //this essentially chops off the body at the position you are scrolled to so the browser can't scroll up any higher $('html').css('overflow','hidden'); } }) //when the user is done and removes focus from the input field .blur(function(){ //checks if it is an iOS device if (is_iOS()){ //Removes the custom styling from the body and html attribute $('body, html').removeAttr('style'); //instantly scrolls the page back down to where you were when you clicked on input field $(document).scrollTop(savedScrollPos); } });
我能够通过添加一个事件监听器到必要的select元素,然后滚动一个像素的偏移量,当有问题的select获得焦点时,可以解决这个问题。
这不一定是一个好的解决scheme,但它比我在这里看到的其他答案更简单,更可靠。 浏览器似乎重新渲染/重新计算位置:固定; 属性基于window.scrollBy()函数中提供的偏移量。
document.querySelector(".someSelect select").on("focus", function() {window.scrollBy(0, 1)});
就像Mark Ryan Salleebuild议的那样,我发现dynamic改变我的背景元素的高度和溢出是关键 – 这使得Safari无法滚动到。
所以在模态开始animation完成后,改变背景的样式:
$('body > #your-background-element').css({ 'overflow': 'hidden', 'height': 0 });
当你closures模式更改回来:
$('body > #your-background-element').css({ 'overflow': 'auto', 'height': 'auto' });
虽然其他答案在简单的上下文中是有用的,但我的DOM太复杂了(谢谢SharePoint)使用绝对/固定位置交换。
这现在已经在iOS 10.3中修复了!
应该不再需要黑客。
干净? 没有。
我最近有一个固定的search字段在一个粘滞的标题自己的问题,你现在可以做的最好的方式是始终保持在一个variables的滚动位置和select使固定元素的位置绝对而不是固定的顶部位置基于文档的滚动位置。
然而,这是非常丑陋的,仍然会导致一些奇怪的来回滚动,然后着陆在正确的地方,但这是我能得到的最接近的。
任何其他解决scheme将涉及覆盖浏览器的默认滚动机制。
没有处理这个特定的错误,但也许把溢出:隐藏; 当文本区域可见时(或者只是活动,取决于您的devise)。 这可能会导致不向浏览器的任何地方“向下”滚动浏览。
一个可能的解决scheme是replaceinput字段。
- 监视一个div上的点击事件
- 集中隐藏的input字段来渲染键盘
- 将隐藏的input字段的内容复制到假input字段中
function focus() { $('#hiddeninput').focus(); } $(document.body).load(focus); $('.fakeinput').bind("click",function() { focus(); }); $("#hiddeninput").bind("keyup blur", function (){ $('.fakeinput .placeholder').html(this.value); });
#hiddeninput { position:fixed; top:0;left:-100vw; opacity:0; height:0px; width:0; } #hiddeninput:focus{ outline:none; } .fakeinput { width:80vw; margin:15px auto; height:38px; border:1px solid #000; color:#000; font-size:18px; padding:12px 15px 10px; display:block; overflow:hidden; } .placeholder { opacity:0.6; vertical-align:middle; }
<input type="text" id="hiddeninput"></input> <div class="fakeinput"> <span class="placeholder">First Name</span> </div>
这些解决scheme都没有为我工作,因为我的DOM是复杂的,我有dynamic的无限滚动页面,所以我必须创build自己的。
背景:我使用一个固定的标题和一个下面的元素,一旦用户向下滚动,就会粘在它下面。 这个元素有一个searchinput字段。 另外,在向前和向后滚动时添加了dynamic页面。
问题:在iOS中,只要用户点击了固定元素中的input,浏览器就会一直滚动到页面的顶部。 这不仅造成了不良的行为,还触发了页面顶部的dynamic页面添加。
预期的解决scheme:当用户点击sticky元素中的input时,在iOS中不滚动(根本没有)。
解:
/*Returns a function, that, as long as it continues to be invoked, will not be triggered. The function will be called after it stops being called for N milliseconds. If `immediate` is passed, trigger the function on the leading edge, instead of the trailing.*/ function debounce(func, wait, immediate) { var timeout; return function () { var context = this, args = arguments; var later = function () { timeout = null; if (!immediate) func.apply(context, args); }; var callNow = immediate && !timeout; clearTimeout(timeout); timeout = setTimeout(later, wait); if (callNow) func.apply(context, args); }; }; function is_iOS() { var iDevices = [ 'iPad Simulator', 'iPhone Simulator', 'iPod Simulator', 'iPad', 'iPhone', 'iPod' ]; while (iDevices.length) { if (navigator.platform === iDevices.pop()) { return true; } } return false; } $(document).on("scrollstop", debounce(function () { //console.log("Stopped scrolling!"); if (is_iOS()) { var yScrollPos = $(document).scrollTop(); if (yScrollPos > 200) { //200 here to offset my fixed header (50px) and top banner (150px) $('#searchBarDiv').css('position', 'absolute'); $('#searchBarDiv').css('top', yScrollPos + 50 + 'px'); //50 for fixed header } else { $('#searchBarDiv').css('position', 'inherit'); } } },250,true)); $(document).on("scrollstart", debounce(function () { //console.log("Started scrolling!"); if (is_iOS()) { var yScrollPos = $(document).scrollTop(); if (yScrollPos > 200) { //200 here to offset my fixed header (50px) and top banner (150px) $('#searchBarDiv').css('position', 'fixed'); $('#searchBarDiv').css('width', '100%'); $('#searchBarDiv').css('top', '50px'); //50 for fixed header } } },250,true));
要求: jQuery mobile是startsroll和stopscroll函数工作所必需的。
反弹是为了消除由粘滞元素产生的任何滞后。
在iOS10中testing
昨天我刚刚跳过这样的事情,当#b可见时,将#a的高度设置为最大可见高度(身高是在我的情况下)
例如:
<script> document.querySelector('#b').addEventListener('focus', function () { document.querySelector('#a').style.height = document.body.clientHeight; }) </script>
ps:对于后来的例子抱歉,只是注意到它是需要的。
我有这个问题,下面的代码行解决了我 –
html{ overflow: scroll; -webkit-overflow-scrolling: touch; }