İOS8'deki Safari, sabit öğeler odaklandığında ekranda kayıyor


96

IOS8 Safari'de konumu sabitlenmiş yeni bir hata var.

Sabit bir paneldeki bir metin alanına odaklanırsanız, safari sizi sayfanın en altına kaydıracaktır.

Sayfanızı tamamen aşağı kaydırmadan ve yerinizi kaybetmeden metin alanlarına metin girmenin hiçbir yolu olmadığı için bu, her tür kullanıcı arayüzüyle çalışmayı imkansız kılar.

Bu hatayı temiz bir şekilde çözmenin bir yolu var mı?

#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>

1
#B'de bir z-endeksi ayarlamak yardımcı olur mu?
Ben

1
z dizini işe yaramaz, belki bazı süslü css dönüşümü yığın bağlamlarında pek olmaz, emin değilim.
Sam Saffron

1
bağlam için burada Söylem üzerine tartışma: meta.discourse.org/t/dealing-with-ios-8-ipad-mobile-safari-bugs/…
Sam Saffron

80
iOS safari yeni IE
geedubb

4
@geedubb kabul etti. varsayılan tarayıcı sürümünü işletim sistemine bağlayan herhangi bir moronik işletim sistemi, son 7 yıldır IE'yi rahatsız eden sorunlara ters düşecektir.
dewd

Yanıtlar:


58

Bu sorunun bu iyi analizine dayanarak, bunu htmlve bodycss'deki öğeleri kullandım :

html,body{
    -webkit-overflow-scrolling : touch !important;
    overflow: auto !important;
    height: 100% !important;
}

Bence benim için harika çalışıyor.


2
benim için de çalıştı. Bu, yükleme sırasında DOM'umu değiştirdiğim için başka birçok şeyi berbat etti, bu yüzden bunu bir sınıfa dönüştürüyorum ve DOM stabilize olduktan sonra html, gövdesine ekliyorum. ScrollTop gibi şeyler çok iyi çalışmıyor (ben otomatik kaydırma yapıyorum), ancak kaydırma işlemlerini yaparken sınıfı ekleyebilir / kaldırabilirsiniz. Yine de Safari ekibinin bir parçası üzerinde kötü çalışma.
Amarsh

1
Bu seçenek şu an İnsanlar da teste isteyebilirsiniz transform: translateZ(0);gelen stackoverflow.com/questions/7808110/...
lkraav

1
Bu sorunu çözer, ancak animasyonlarınız varsa çok dalgalı görüneceklerdir. Bir medya sorgusuna sarmak daha iyi olabilir.
mmla

Benim için iOS 10.3'te çalıştı.
alıntılarBro

Problemi çözmez. Sanal klavye göründüğünde kaydırmayı
kesmeniz

36

Bulabildiğim en iyi çözüm position: absolute;, odakta kullanmaya geçmek ve kullanırken bulunduğu konumu hesaplamaktır position: fixed;. İşin püf noktası, focusolayın çok geç tetiklenmesidir, bu yüzden touchstartkullanılmalıdır.

Bu cevaptaki çözüm, iOS 7'de sahip olduğumuz doğru davranışı çok yakından taklit ediyor.

Gereksinimler:

bodyEleman mutlak konumlandırma geçer Eleman doğru konumlandırmayı garanti etmek için konumlandırma olmalıdır.

body {
    position: relative;
}

Kod ( Canlı Örnek ):

Aşağıdaki kod, sağlanan test senaryosu için temel bir örnektir ve özel kullanım durumunuza göre uyarlanabilir.

//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);
});

İşte karşılaştırma için hack içermeyen aynı kod .

Uyarı:

position: fixed;Öğe, konumlandırmanın yanı sıra başka ana öğelere sahipse, öğesine bodygeçiş position: absolute;beklenmedik davranışlara neden olabilir. Bunun doğası gereği, position: fixed;bu tür öğelerin iç içe geçmesi yaygın olmadığı için muhtemelen önemli bir sorun değildir.

Öneriler:

touchstartEtkinliğin kullanımı çoğu masaüstü ortamını filtreleyecek olsa da , muhtemelen kullanıcı aracısı koklamayı kullanmak isteyeceksiniz, böylece bu kod yalnızca bozuk iOS 8 için çalışacak ve Android ve daha eski iOS sürümleri gibi diğer cihazlar için çalışmayacaktır. Ne yazık ki, Apple'ın bu sorunu iOS'ta ne zaman çözeceğini henüz bilmiyoruz, ancak bir sonraki ana sürümde düzeltilmezse şaşırırdım.


Bir div ile çift sarmanın ve şeffaf sarma div'in üzerinde yüksekliği% 100 olarak ayarlamanın bundan kaçınmak için kandırıp kandırmayacağını merak ediyorum ...
Sam Saffron

@SamSaffron Böyle bir tekniğin nasıl işe yarayabileceğini açıklayabilir misiniz? Bunun gibi birkaç şeyi başarılı olmadan denedim. Belgenin yüksekliği belirsiz olduğundan, nasıl çalışacağından emin değilim.
Alexander O'Mara

Ben sadece "sabit" bir% 100 boy sarmalayıcının bu
Sam Saffron

@downvoter: Bir şeyi yanlış mı anladım? Bunun korkunç bir çözüm olduğuna katılıyorum, ancak daha iyilerinin olduğunu düşünmüyorum.
Alexander O'Mara

4
Bu benim için işe yaramadı, giriş alanı hala hareket ediyor.
Rodrigo Ruiz

8

Mutlak konuma geçmeye gerek kalmadan çalışan bir yöntem buldum !

Tam yorumlanmamış kod

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);
    }
});

Parçalıyorum

Öncelikle HTML'de sayfanın üst kısmına doğru sabit giriş alanına sahip olmanız gerekir (bu sabit bir öğedir, bu nedenle yine de üst tarafa yakın olması anlamsal olarak mantıklı olmalıdır):

<!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>

Ardından, mevcut kaydırma konumunu global değişkenlere kaydetmeniz gerekir:

//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;

Ardından, düzeltmeye ihtiyaç duymayan şeyleri etkilememesi için iOS cihazlarını algılamanın bir yoluna ihtiyacınız var (işlev https://stackoverflow.com/a/9039885/1611058 adresinden alınmıştır )

//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;
}

Artık ihtiyacımız olan her şeye sahip olduğumuza göre, işte düzeltme :)

//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);
    }
});

+1. Önemsiz olmayan bir DOM hiyerarşiniz varsa, bu, kabul edilen yanıttan çok daha az karmaşık bir düzeltmedir. Bunun daha fazla olumlu oyu olmalı
Anson Kao

Bunu yerel JS'de de sağlayabilir misiniz? Çok teşekkürler!
mesqueeb

@SamSaffron, bu cevap gerçekten senin için çalıştı mı? burada bir örnek alabilir miyim? benim için işe yaramadı mı?
Ganesh Putta

@ SamSaffron, bu cevap gerçekten probleminizi çözdü mü, sizin için işe yarayan bir örnek gönderebilir misiniz, aynı üzerinde çalışıyorum, ama benim için işe yaramadı.
Ganesh Putta

@GaneshPutta Daha yeni bir iOS güncellemesinin bunun artık çalışmamasını sağlaması mümkündür. Bunu 2,5 yıl önce gönderdim. Tüm talimatları tam olarak izlediyseniz, yine de çalışmalıdır: /
Daniel Tonon

4

Gerekli seçim öğelerine bir olay dinleyicisi ekleyerek, ardından söz konusu seçim odak kazandığında bir piksel kayması kadar kaydırarak bunu belirli girişler için düzeltebildim.

Bu mutlaka iyi bir çözüm değil, ancak burada gördüğüm diğer cevaplardan çok daha basit ve daha güvenilir. Tarayıcı konumu yeniden işliyor / yeniden hesaplıyor gibi görünüyor: düzeltildi; window.scrollBy () işlevinde sağlanan ofseti temel alan özellik.

document.querySelector(".someSelect select").on("focus", function() {window.scrollBy(0, 1)});

2

Mark Ryan Sallee'nin önerdiği gibi, arka plan öğemin yüksekliğini ve taşmasını dinamik olarak değiştirmenin anahtar olduğunu anladım - bu, Safari'ye kaydıracak hiçbir şey vermiyor.

Modun açılış animasyonu bittikten sonra, arka plan stilini değiştirin:

$('body > #your-background-element').css({
  'overflow': 'hidden',
  'height': 0
});

Modu kapattığınızda geri değiştirin:

$('body > #your-background-element').css({
  'overflow': 'auto',
  'height': 'auto'
});

Diğer yanıtlar daha basit bağlamlarda yararlı olsa da, DOM'm mutlak / sabit konum takasını kullanmak için çok karmaşıktı (SharePoint sayesinde).


1

Temiz? Hayır.

Son zamanlarda bu sorunu kendimde yapışkan bir başlıkta sabit bir arama alanıyla yaşadım, şu anda yapabileceğiniz en iyi şey kaydırma konumunu her zaman bir değişkende tutmak ve seçim üzerine sabit öğenin konumunu bir üst ile sabitlemek yerine mutlak yapmaktır. belgenin kaydırma konumuna göre konum.

Ancak bu çok çirkin ve yine de doğru yere inmeden önce bazı garip ileri geri kaydırma ile sonuçlanıyor, ancak alabileceğim en yakın şey bu.

Diğer herhangi bir çözüm, tarayıcının varsayılan kaydırma mekaniğinin geçersiz kılınmasını içerir.


0

Bu özel hatayla ilgilenmedim, ancak bir taşma olabilir: gizli; metin alanı göründüğünde (veya tasarımınıza bağlı olarak yalnızca etkinken) gövde üzerinde. Bu, tarayıcıya kaydırmak için "aşağı" herhangi bir yer vermeme etkisine sahip olabilir.


1
Bu hack'i bile düşünecek kadar erken tetiklemek için touchstart'ı bile alamıyorum :(
Sam Saffron

0

Olası bir çözüm, giriş alanını değiştirmek olacaktır.

  • Bir div'deki tıklama olaylarını izleyin
  • klavyeyi oluşturmak için gizli bir giriş alanına odaklanın
  • gizli giriş alanının içeriğini sahte giriş alanına çoğaltın

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> 


Codepen


0

Bu çözümlerin hiçbiri benim için işe yaramadı çünkü DOM'm karmaşık ve dinamik sonsuz kaydırma sayfalarım var, bu yüzden kendi başıma oluşturmak zorunda kaldım.

Arka plan: Sabit bir başlık ve daha aşağıda, kullanıcı o kadar aşağı kaydırdığında altında kalan bir öğe kullanıyorum. Bu elemanın bir arama giriş alanı vardır. Ek olarak, ileri ve geri kaydırma sırasında eklenen dinamik sayfalarım var.

Sorun: iOS'ta, kullanıcı sabit öğedeki girişi her tıkladığında, tarayıcı sayfanın en üstüne kadar kayıyordu. Bu sadece istenmeyen davranışlara neden olmadı, aynı zamanda sayfanın üst kısmına dinamik sayfa eklememi de tetikledi.

Beklenen Çözüm: Kullanıcı yapışkan öğedeki girdiye tıkladığında iOS'ta kaydırma yok (hiç yok).

Çözüm:

     /*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));

Gereksinimler: Başlatma ve kaydırma kaydırma işlevlerinin çalışması için JQuery mobile gereklidir.

Yapışkan öğenin yarattığı herhangi bir gecikmeyi düzeltmek için geri tepme dahildir.

İOS10'da test edilmiştir.


0

Dün #b göründüğünde #a yüksekliğini maksimum görünür yüksekliğe (benim durumumda vücut yüksekliği) ayarlayarak böyle bir şeyin üzerinden atladım.

ör .:

    <script>
    document.querySelector('#b').addEventListener('focus', function () {
      document.querySelector('#a').style.height = document.body.clientHeight;
    })
    </script>

ps: geç örnek için üzgünüm, sadece gerekli olduğunu fark ettim.


14
Lütfen düzeltmenizin nasıl yardımcı olabileceğini netleştirmek için bir kod örneği ekleyin
roo2

@EruPenkman özür dilerim yorumunuzu fark ettim, umarım yardımcı olur.
Onur Uyar

0

Bu artık iOS 10.3'te düzeltildi!

Hack'lere artık ihtiyaç duyulmamalıdır.


1
Bunun düzeltildiğini gösteren herhangi bir sürüm notuna işaret edebilir misiniz?
bluepnume

Apple çok gizli, hata raporumu kapattılar Şimdi düzgün çalıştığını doğruladım, elimde olan tek şey bu :)
Sam Saffron

1
Bu sorunu hala iOS 11'de
yaşıyorum

Hayır, bu iOS 13'te bile sorun
olmaya devam ediyor

0

Sorunu yaşadım, aşağıdaki kod satırları benim için çözdü -

html{

 overflow: scroll; 
-webkit-overflow-scrolling: touch;

}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.