program tip

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

radiobox 2020. 9. 6. 09:19
반응형

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 덕분에).


깨끗하게? 아니.

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

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

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


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

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


Haven't dealt with this particular bug, but maybe put an overflow: hidden; on the body when the text area is visible (or just active, depending on your design). This may have the effect of not giving the browser anywhere "down" to scroll to.


A possible solution would be to replace the input field.

  • Monitor click events on a div
  • focus a hidden input field to render the keyboard
  • replicate the content of the hidden input field into the fake input field

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


None of these solutions worked for me because my DOM is complicated and I have dynamic infinite scroll pages, so I had to create my own.

Background: I am using a fixed header and an element further down that sticks below it once the user scrolls that far down. This element has a search input field. In addition, I have dynamic pages added during forward and backwards scroll.

Problem: In iOS, anytime the user clicked on the input in the fixed element, the browser would scroll all the way to the top of the page. This not only caused undesired behavior, it also triggered my dynamic page add at the top of the page.

Expected Solution: No scroll in iOS (none at all) when the user clicks on the input in the sticky element.

Solution:

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

Requirements: JQuery mobile is required for the startsroll and stopscroll functions to work.

Debounce is included to smooth out any lag created by the sticky element.

Tested in iOS10.


I just jumped over something like this yesterday by setting height of #a to max visible height (body height was in my case) when #b is visible

ex:

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

ps: sorry for late example, just noticed it was needed.


I had the issue, below lines of code resolved it for me -

html{

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

}

참고URL : https://stackoverflow.com/questions/29001977/safari-in-ios8-is-scrolling-screen-when-fixed-elements-get-focus

반응형