iOS8의 Safari는 고정 요소가 포커스를받을 때 화면을 스크롤합니다.


96

IOS8 Safari에는 위치가 수정 된 새로운 버그가 있습니다.

고정 된 패널에있는 텍스트 영역에 초점을 맞추면 safari는 페이지 하단으로 스크롤합니다.

이렇게하면 페이지를 끝까지 스크롤하고 위치를 잃지 않고는 텍스트 영역에 텍스트를 입력 할 방법이 없기 때문에 모든 종류의 UI를 사용할 수 없습니다.

이 버그를 깔끔하게 해결할 수있는 방법이 있습니까?

#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에 Z- 색인을 설정하면 도움이 되나요?

1
z 인덱스는 도움이되지 않습니다. 아마도 스택 컨텍스트를 사용하면 어떤 멋진 no op css 변환이 확실하지 않을 것입니다.
Sam Saffron 2015 년

1
컨텍스트 여기 담론에 대한 논의는 다음과 같습니다 meta.discourse.org/t/dealing-with-ios-8-ipad-mobile-safari-bugs/...
샘 사프란

80
iOS의 사파리는 새로운 IE입니다
geedubb

4
@geedubb이 동의했습니다. 기본 브라우저 버전을 OS에 묶는 모든 멍청한 OS는 지난 7 년 동안 IE를 괴롭혔던 문제를 더럽힐 것입니다.
dewd

답변:


58

이를 바탕으로 좋은 분석 이 문제에, 나는이를 사용했습니다 htmlbodyCSS 요소 :

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

나는 그것이 나를 위해 잘 작동한다고 생각합니다.


2
나를 위해 일했습니다. 이것은로드시 DOM을 조작하고 있기 때문에 다른 많은 것들을 망쳐 놓았습니다. 그래서 이것을 클래스로 만들고 DOM이 안정화 된 후 html, body에 추가했습니다. scrollTop과 같은 것은 잘 작동하지 않지만 (자동 스크롤을하고 있습니다) 스크롤 작업을 수행하는 동안 클래스를 추가 / 제거 할 수 있습니다. 그래도 Safari 팀의 일이 좋지 않습니다.
Amarsh

1
이 옵션을보고있는 사람들 transform: translateZ(0);stackoverflow.com/questions/7808110/
lkraav

1
이렇게하면 문제가 해결되지만 애니메이션이 있으면 매우 고르지 않게 보입니다. 미디어 쿼리로 래핑하는 것이 더 나을 수 있습니다.
mmla

iOS 10.3에서 나를 위해 일했습니다.
quotesBro

문제가 해결되지 않습니다. 당신은 스크롤 절편을 필요로 할 때까지 변경 높이 특정 값에 가상 키보드 쇼 : stackoverflow.com/a/46044341/84661
브라이언 Cannard

36

내가 생각해 낼 수있는 가장 좋은 해결책은 position: absolute;초점 을 사용하도록 전환하고 position: fixed;. 트릭은 focus이벤트가 너무 늦게 실행되므로 touchstart사용해야한다는 것입니다.

이 답변의 솔루션은 iOS 7에서 수행 한 올바른 동작을 매우 가깝게 모방합니다.

요구 사항 :

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이벤트를 사용 하면 대부분의 데스크톱 환경이 필터링 되지만 이 코드는 Android 및 이전 iOS 버전과 같은 다른 장치가 아닌 손상된 iOS 8에서만 실행되도록 사용자 에이전트 스니핑을 사용하는 것이 좋습니다. 안타깝게도 Apple이 iOS에서이 문제를 언제 해결할지 아직 알 수 없지만 다음 메이저 버전에서 해결되지 않으면 놀랄 것입니다.


투명 포장 사업부에서 100 % 사업부와 설정 높이 이중 포장이 ...이 피로를 속일 수 있을까
샘 사프란

@SamSaffron 그러한 기술이 어떻게 작동 할 수 있는지 명확히 해주시겠습니까? 나는 성공하지 않고 이와 같은 몇 가지를 시도했습니다. 문서의 높이가 모호하기 때문에 어떻게 작동 할 수 있을지 모르겠습니다.
Alexander O'Mara

나는이 문제를 해결할 수있다 "고정"100 % 높이 래퍼 가능하지 않은 단순히 생각
샘 사프란

@downvoter : 내가 뭔가 잘못 되었나요? 나는 이것이 끔찍한 해결책이라는 데 동의하지만 더 나은 해결책은 없다고 생각합니다.
Alexander O'Mara 2015 년

4
이것은 나를 위해 작동하지 않았고 입력 필드는 여전히 움직입니다.
Rodrigo Ruiz

8

절대 위치로 변경할 필요없이 작동하는 방법을 찾았습니다 !

주석 처리되지 않은 전체 코드

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

+1. 이것은 사소한 DOM 계층 구조가있는 경우 허용되는 답변보다 훨씬 덜 복잡한 수정입니다. 이것은 더 upvotes을 가져야한다
베어 카오에게

네이티브 JS에서도 이것을 제공 할 수 있습니까? 정말 고마워!
mesqueeb

@ SamSaffron,이 답변이 정말 당신을 위해 일했습니까? 여기에 몇 가지 예를 들어도 될까요? 그것은 나를 위해 일했습니까?
Ganesh Putta 2018

@ SamSaffron,이 답변이 실제로 문제를 해결 했습니까? U를 위해 일한 몇 가지 예를 보낼 수 있습니까? 동일하게 작업하고 있지만 저에게 효과적이었습니다.
Ganesh Putta 2018

@GaneshPutta 최신 iOS 업데이트로 인해 더 이상 작동하지 않을 수 있습니다. 나는 이것을 2.5 년 전에 게시했다. 모든 지침을 정확히 따랐다면 여전히 작동합니다. : /
Daniel Tonon

4

필요한 선택 요소에 이벤트 리스너를 추가 한 다음 해당 선택이 포커스를 얻었을 때 한 픽셀의 오프셋만큼 스크롤하여 선택 입력에 대해이 문제를 해결할 수있었습니다.

이것은 반드시 좋은 해결책은 아니지만 여기에서 본 다른 답변보다 훨씬 간단하고 신뢰할 수 있습니다. 브라우저가 위치를 다시 렌더링 / 다시 계산하는 것 같습니다. 고정; window.scrollBy () 함수에 제공된 오프셋을 기반으로하는 속성입니다.

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

2

Mark Ryan Sallee가 제안한 것처럼, 내 배경 요소 의 높이와 오버플로를 동적으로 변경하는 것이 핵심이라는 것을 알았 습니다. 이것은 Safari에서 스크롤 할 수있는 항목을 제공하지 않습니다.

따라서 모달의 시작 애니메이션이 완료된 후 배경 스타일을 변경합니다.

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

모달을 닫으면 다시 변경하십시오.

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

다른 답변은 더 간단한 컨텍스트에서 유용하지만 절대 / 고정 위치 스왑을 사용하기에는 DOM이 너무 복잡했습니다 (SharePoint 덕분에).


1

깨끗하게? 아니.

최근에 고정 된 헤더에 고정 된 검색 필드를 사용하여이 문제가 발생했습니다. 현재 할 수있는 최선은 스크롤 위치를 항상 변수에 유지하고 선택시 고정 요소의 위치를 ​​상단으로 고정하는 대신 절대적으로 만드는 것입니다. 문서의 스크롤 위치에 따라 위치.

그러나 이것은 매우 추하고 올바른 위치에 착륙하기 전에 이상한 앞뒤로 스크롤되는 결과를 초래하지만 가장 가까운 곳입니다.

다른 솔루션은 브라우저의 기본 스크롤 메커니즘을 재정의하는 것입니다.


0

이 특정 버그를 다루지는 않았지만 오버플로를 넣을 수 있습니다 : hidden; 텍스트 영역이 표시 될 때 (또는 디자인에 따라 활성 상태 일 때) 본문에. 이것은 스크롤 할 "아래로"브라우저를 제공하지 않는 효과가있을 수 있습니다.


1
난 충분히 일찍 심지어 해킹 :( 고려에 트리거 touchstart를 얻을 수가 캔트
샘 사프란

0

가능한 해결책은 입력 필드를 바꾸는 것입니다.

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


코드 펜


0

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

요구 사항 : startsroll 및 stopscroll 함수가 작동하려면 JQuery 모바일이 필요합니다.

디 바운스는 고정 요소로 인한 지연을 완화하기 위해 포함됩니다.

iOS10에서 테스트되었습니다.


0

어제 #b가 표시 될 때 #a의 높이를 최대 가시 높이 (제 경우에는 신체 높이)로 설정하여 이와 같은 것을 뛰어 넘었습니다.

전의:

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

추신 : 늦은 예를 들어 죄송합니다.


14
수정 사항이 어떻게 도움이 될 수 있는지 명확히하기 위해 코드 예제를 포함하십시오
roo2

@EruPenkman 죄송합니다 방금 귀하의 의견을 발견, 도움이되기를 바랍니다.
Onur Uyar

0

이제 iOS 10.3에서 수정되었습니다!

더 이상 해킹이 필요하지 않습니다.


1
이 문제가 수정되었다고 지적하는 릴리스 정보를 가리킬 수 있습니까?
bluepnume

애플은 매우 비밀, 그들은 :) 모두 내가 가진입니다, 내가 지금 제대로 작동 확인 내 버그 리포트를 폐쇄
샘 사프란

1
iOS 11
여전히이

아니요, iOS 13에서도 여전히 문제가됩니다.
Dmitriy Khudorozhkov

0

코드 줄 아래에서 문제가 해결되었습니다.

html{

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

}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.