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

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>



답변

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

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

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


답변

내가 생각해 낼 수있는 가장 좋은 해결책은 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에서이 문제를 언제 해결할지 아직 알 수 없지만 다음 메이저 버전에서 해결되지 않으면 놀랄 것입니다.


답변

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

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

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


답변

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

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

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


답변

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

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

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

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

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

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


답변

깨끗하게? 아니.

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

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

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


답변

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