Скроллинг фона в модальных окнах на Iphone

или

Prevent background body scroll in IOS

Много наслышаны про задачу запретить скроллить фон при открытых диалогах и модальных окон. Много крови попило данное решение Apple встроить собственную поддержку прокрутки. При чем от версии к версии изобретают и падают существующие решения.

Причем данная ситуация воспроизводится только на самих устройствах Iphone либо в эмуляторах. Вы не сможете воспроизвести ее в Chrome debugger emulator

Дело в том что body в большинстве своем на сайте - имеет прокрутку. т/к/ высота контента как правило занимает больше одного экрана. Следовательно - вы можете его скроллить. И исчезает ползунок для скролла с виду не сразу и пока он активен - вы сможете за него ухватиться и продолжить скроллинг, чего бы не было наложено поверх данного слоя с прокруткой.

Все думают, что повесив overlay - смогут запретить доступ к фону, а не тут то было. Apple всех перехитрил.

Получается, что единственный способ запретить скроллинг фона - это убрать скроллинг этого самого слоя. Делается это легко. Overflow-y: none;

Авторы vuetify тоже так думали когда встраивали данный стиль

html.overflow-y-hidden {
overflow-y: hidden !important;
}

и что? Думали Apple так прост. Не тут то было

Хорошо, скажите вы. Это же не единственный способ запретить прокрутку

Давайте добавим

body{
position: fixed;
}

И это зашло. Да прокрутка исчезла. Выкуси, создатель движка скроллинга apple

Мы можем при открытии диалога - вешать такой стиль на тело и оно будет заблокировано для скроллинга

Только каждый раз когда мы так делаем - скроллинг сбрасывается и страница прыгает в самый верх. Это не приятно и не красиво

У нас же Javascript, неужели он не достаточно мощный чтобы исправить все эти неисправности?

Давайте запоминать позицию и возвращаться к ней в момент открытия и закрытия диалога. Сказано - сделано

Показываю код на примере mixin для vue с vuetify

export default {

 data() {

   return {

     dialogScroll: 0,

   }

 },

 

 watch: {

   dialog(value) {

     if (value) {

       this.dialogScroll = window.scrollY

       document.body.classList.add('fixed')

     } else {

       this.$nextTick(() => window.scrollTo({ top: this.dialogScroll }))

       document.body.classList.remove('fixed')

     }

   },

 },

}

Само собой

.fixed {

 position: fixed;

}

 

Вы все равно не будете это проверять, вам слишком лень. А мне лень создавать рабочие примеры. Просто достаточно посмотреть на код чтобы понять что оно будет работать как задумано.

Зачем nextTick? Просто возврат должен проходить после того как мы уберем fixed с body, а иначе оно не будет возвращаться

Работает. ДА оно работает. Javascript победил

Только дергается как-то оно. Диалог открывается через анимацию и в это время видно что страница дергается. Очень не приятно. Чем мы можем ответить на это?

Конечно мы можем сместить страницу. Первое что приходит в голову. Но как?

Можем сместить контент через transform: Transition

И оно смещает, но смещает вместе с диалогом, чего не хотелось бы

Мы также можем просто сместить содержание body через отступ. Хорошая мысль

export default {
data() {
return {
dialogScroll: 0,
}
},

watch: {
dialog(value) {
if (value) {
this.dialogScroll = window.scrollY
document.body.classList.add('fixed')
document.body.style.marginTop = '-'+this.dialogScroll+'px'
} else {
this.$nextTick(() => window.scrollTo({ top: this.dialogScroll }))
document.body.classList.remove('fixed')
document.body.style.marginTop = null
}
},
},
}

И еще обработчик на случай если у вас диалог может исчезнуть при переходе назад или при переходе по ссылке с уничтожением диалога. Конечный вариант скрипта выглядит вот так со всеми бантиками

export default {
data() {
return {
dialogScroll: 0,
}
},

beforeDestroy() {
this.scrollRelease()
},

methods:{
scrollLock(){
this.dialogScroll = window.scrollY
document.body.classList.add('fixed')
document.body.style.marginTop = '-'+this.dialogScroll+'px'
},
scrollRelease(){
this.$nextTick(() => window.scrollTo({ top: this.dialogScroll }))
document.body.classList.remove('fixed')
document.body.style.marginTop = null
}
},

watch: {
dialog(value) {
if (value) {
this.scrollLock()
} else {
this.scrollRelease()
}
},
},
}

Да это помогло. Сейчас всё гладко, за исключением случаев когда у вас диалоги открываются через vuex, там уже другая подвязка через mapState

Нужно просто реактивно подвязываться не через v-model а через :value и @input

 

Но это уже другая тема

 <v-dialog

   :value="dialog"

   @input="value => $store.commit('SET_DIALOG', value)"

 >

 

Надеюсь, помог

Успехов в покорении изысков IOS

Если вам удалось решить проблему по другому - накидывайте на вентилятор в комментариях

  • Автор: kosmom
  • Рейтинг: 1
  • Просмотров: 761
  • Комментариев: 0
  • Создан: 27.04.2021 10:54

Комментарии (0)