Скроллинг фона в модальных окнах на 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
  • Просмотров: 1034
  • Комментариев: 0
  • Создан: 27.04.2021 10:54

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

Ваши предложения и пожелания пишите на pro@kosmom.ru

Теги

ajax axios backup bootstrap core framework eloquent excel home project html ios javascript keep-alive kpi laravel legacy mvp orm php rip scroll solid timestamp undefined vue vuetify watch безопасность биометрический паспорт ваша любаша для путешествий загран на 10 лет загран паспорт загранпаспорт нового образца зимние книги как заполнить анкеты кеширование книги на новый год логирование мцф недвижимость новогодние книги образец заполнения антеты паспорт для путешествий паспорт нового поколения печать продукт проектирование прокси разработка ремонт ремонт в апартаментах ремонт нежилого помещения самокат сдача сколько стоил ремонт апартаментов спорт стандарты таблица финансы хостинг цена ремонта что почитать зимой юзабилити

Случайный пост

07.12.2015 17:22
Книги за ноябрь 2015 года