Safari в ios8 прокручивает экран, когда фиксированные элементы получают фокус
в iOS8 Safari появилась новая ошибка с фиксированной позицией.
Если вы фокусируете текстовое поле, которое находится в фиксированной панели, safari прокручивает вас до нижней части страницы.
это делает все виды UIs невозможно работать, так как у вас нет никакого способа ввода текста в текстовые области без прокрутки страницы полностью вниз и потерять свое место.
есть ли способ обойти эту ошибку чисто?
#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>
12 ответов:
на основе этого хороший анализ из этой проблемы, я использовал это в
htmlиbodyэлементы в css:html,body{ -webkit-overflow-scrolling : touch !important; overflow: auto !important; height: 100% !important; }Я думаю, что это работает отлично для меня.
лучшее решение, которое я мог бы придумать, это переключиться на использование
position: absolute;при фокусировке и вычислении положения, в котором он находился, когда он использовалposition: fixed;. Фокус в том, чтоfocusсобытие срабатывает слишком поздно, такtouchstartнеобходимо использовать.решение в этом ответе очень точно имитирует правильное поведение, которое мы имели в iOS 7.
требования:
The
bodyэлемент должен иметь позиционирование для обеспечения правильного позиционирования, когда элемент переключается на абсолютное позиционирование.body { position: relative; }Код (Видео):
следующий код является базовым примером для предоставленного тестового случая и может быть адаптирован для вашего конкретного случая использования.
//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;Это, вероятно, не является серьезной проблемой, так как вложенность таких элементов не является общей.рекомендации:
при использовании
touchstartсобытие будет отфильтровывать большинство настольных сред, вы, вероятно, захотите использовать User-agent sniffing, чтобы этот код работал только для сломанной iOS 8, а не для других устройств, таких как Android и более старые версии iOS. К сожалению, мы пока не знаем когда Apple исправит эту проблему в 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 (это фиксированный элемент, поэтому он должен семантически иметь смысл иметь его в верхней части в любом случае):
<!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>затем вам нужно сохранить текущую позицию прокрутки в глобальные переменные:
//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 inputs, добавив прослушиватель событий к необходимым элементам select, а затем прокручивая смещение на один пиксель, когда рассматриваемый выбор получает фокус.
Это не обязательно хорошее решение, но гораздо проще и надежнее, чем другие ответы, которые я видел здесь. Браузер, похоже, повторно отображает / повторно вычисляет позицию: фиксированный; атрибут на основе смещения, предоставленного в окне.scrollBy (функция).
document.querySelector(".someSelect select").on("focus", function() {window.scrollBy(0, 1)});
Так же, как предложил Марк Райан Салли, я обнаружил, что динамически меняется высота и переполнение моего фоновый элемент это ключ-это не дает Safari ничего для прокрутки.
поэтому после завершения анимации открытия модала измените стиль фона:
$('body > #your-background-element').css({ 'overflow': 'hidden', 'height': 0 });когда вы закрываете модальный изменить его обратно:
$('body > #your-background-element').css({ 'overflow': 'auto', 'height': 'auto' });в то время как другие ответы полезны в более простых контекстах, мой DOM был слишком сложным (благодаря SharePoint), чтобы используйте абсолютный / фиксированный позиционный своп.
чистоплотны? нет.
недавно у меня была эта проблема с фиксированным полем поиска в липком заголовке, лучшее, что вы можете сделать на данный момент, - это сохранить положение прокрутки в переменной во все времена и при выборе сделать положение фиксированного элемента абсолютным, а не фиксированным с верхней позицией на основе положения прокрутки документа.
Это, однако, очень некрасиво и все еще приводит к какой-то странной прокрутке назад и вперед перед посадкой в нужном месте, но это самое близкое, что я мог получить.
любое другое решение будет включать в себя переопределение механики прокрутки по умолчанию браузера.
не имели дело с этой конкретной ошибкой, но, возможно, поставить overflow: hidden; на теле, когда текстовая область видна (или просто активна, в зависимости от вашего дизайна). Это может иметь эффект не давая браузеру в любом месте "вниз"для прокрутки.
возможным решением было бы заменить на поле ввода.
- монитор нажмите события на div
- сфокусируйте скрытое поле ввода для отображения клавиатуры
- скопировать содержимое скрытого поля ввода в поддельный поле ввода
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>
ни одно из этих решений не работало для меня, потому что мой DOM сложен, и у меня есть динамические бесконечные страницы прокрутки, поэтому мне пришлось создать свой собственный.
Справочная информация: я использую фиксированный заголовок и элемент ниже, который прилипает под ним, как только пользователь прокручивает это далеко вниз. Этот элемент имеет поле ввода поиска. Кроме того, у меня есть динамические страницы, добавленные во время прокрутки вперед и назад.
: в iOS, в любое время пользователь нажал на ввод в фиксированном элементе, браузер будет прокручивать весь путь до верхней части страницы. Это не только вызвало нежелательное поведение, но и вызвало добавление моей динамической страницы в верхней части страницы.
Предполагаемое Решение: нет прокрутки в 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 функции для работы.
Debounce включен, чтобы сгладить любое отставание, созданное липким элементом.
протестировано в iOS10.
Я просто перепрыгнул через что-то вроде этого вчера, установив высоту #a до максимальной видимой высоты (высота тела была в моем случае), когда #b видно
ex:
<script> document.querySelector('#b').addEventListener('focus', function () { document.querySelector('#a').style.height = document.body.clientHeight; }) </script>ps: извините за поздний пример, просто заметил, что это было необходимо.
У меня была проблема, ниже строки кода решили ее для меня -
html{ overflow: scroll; -webkit-overflow-scrolling: touch; }
Comments