Popup на jquery c відключенням скрол основного контенту

Зовсім недавно зіткнувся з не зовсім звичайним ТАСК в таскменеджере. ПроджектМенеджер просто безпосередньо скопіював текст замовника - «Хочу, щоб попап був як в вконтакте». Що значить «як в вконтакте» я зрозумів не відразу. «Потестів» попап в соц.сети я зрозумів, чого ж від мене хочуть - при відкритому попап основний контент не повинен Скролл, але в той же час, якщо розміри попап більше, ніж вікно користувача - повинен Скролл попап (основний контент природно так само НЕ скролл і в цьому випадку).
Складного в цьому ТАСК я нічого не побачив і приступив до його виконання. Логіка була наступна - при откртіі попап даємо bodyoverflow = hidden. а при закритті природно його повертаємо в auto. Сам попап кладемо в div-обгортку і даємо йому overflow = auto. щоб у випадках, коли розмір вікна менше, ніж розмір попап - користувач міг його скролл.
Під OS X все працювало ідеально. Але я не врахував одного нюансу - в інших системах в браузері є постійно видимий скролл, який має свою ширину. І якщо просто робити блоку, у якого є скролл, overflow hidden - скролл пропадає, ширина блоку стає більше на ширину скролла і контент «стрибає». Тоді я вирішив написати плагін, який вирішує і цю проблему. Ще раз подивившись, як працює цей дивовижний попап в вконтакте, я зрозумів логіку і приступив до виконання.

(Мені здається, сайти, які не використовують jQuery - вже рідкість. Тому писав я плагін з використанням jQuery)
Так як я хотів написати такий плагін, для роботи якого не потрібно було дописувати якісь конструкції в Html і в css, плагін повинен це все зробити сам. Для початку варто розібратися, який Html-каркас буде потрібен плагіну. Так як у попап повинен бути фон, котрих буде перекривати основний контент, в body повинен з'явитися div, який і буде безпосередньо самим фоном. Це перший елемент. Другий - обгортка попап. Вона потрібна для того, щоб під час, коли у body варто overflow hidden, якщо наш попап більше за розміром, ніж розмір вікна користувача - користувач міг проскроліть контейнер з попап. Для цього у оболонки буде прописано css властивість overflow рівне auto. Тепер потрібно організвать роботу плагіна так, щоб він підставляв ці об'єкти в наш html.
За роботу з попап відповідатиме об'єкт _JPopup. У нього ж є метод init (), який буде «смикатися» після завантаження сторінки. У цьому методі плагін і буде додавати потрібні для його роботи html теги в body.

Так само при ініціалізації попап варто подумати і про те, як він буде закриватися. Мені здається, що більшість користувачів вже звикло до того, що при натисканні на контент за межами попап сам попап повинен закритися. У нашому випадку користувач не зможе натиснути на будь-якої контент за межами блоку $ popupBlock, так як у нього 100-відсоткові розміри і найбільший z-index. Виходить, що потрібно закривати попап після кліка на $ popupBlock. Для цього додаємо в метод init () наступний код:

Сам метод _JPopup.hidePopup () ми розберемо трохи пізніше.
Тепер приступимо до найцікавішого - появи попап. В першу чергу нам потрібно знати, який блок буде попап. Є багато варіантів, як реалізувати вказівку плагіну на те, який блок потрібно брати. Наприклад передавати селектор як параметр, або ж передавати безпосередньо сам jQuery об'єкт. Мені ж подобається працювати з методами jQuery і тому хотілося отримати можливість викликати появу попап подібним способом: $ ( '# someId'). ShowPopup (); Для цього додамо метод jQuery:

Все, що робить даний метод - викликає метод об'єкта _JPopup showPopup () і передає йому об'єкт jQuery елемента, до якого була застосована функція. Потім, щоб не порушувати можливість ведення ланцюжка методів, повертаємо this.
А ось метод _JPopup.showPopup () і буде показувати потрібний нам попап.

Розберемо цей метод. З самого початку ми показуємо напівпрозорий фон. У разі, якщо користувач використовує IE8 і нижче, фон просто з'явиться, якщо ж користувач використовує більш цивілізований браузер - фон з'явиться плавно і буде напівпрозорим за рахунок css властивості opacity. Плавне поява ми добиваємося за допомогою методу fadeTo ( 'slow', 0.7). Якщо контент сторінки буде проскроллен, і так як у нашого фону прописаний position: absolute, він не перекриє весь контент. Для того, щоб такого не сталося, змінимо його css значення top і left рівно на стільки, на скільки був проскроллен документ.
Потім, ми відключаємо скролл у body прописуючи 'overflow': 'hidden' і в той же час даємо йому ширину, рівну ширині документа мінус ширину скролла. Так як в різних ОС і різних браузерах ширина скролла може бути різна (десь вона взагалі дорівнює нулю), ми повинні її (ширину) визначити. Для цього я скористався кодом, який колись десь знайшов. Чесно кажучи вже навіть не пам'ятаю де. Але він досі вірно допомагає мені у визначенні ширини скролла. З огляду на, що ширина скролла не може змінюватися в одному браузері, для того, щоб кожен раз не витрачати час на її знаходження, ми створимо змінну, рівну null, і тільки в разі якщо вона дорівнює null будемо визначати ширину скролла і привласнювати це значення цієї змінної . У такому випадку при повторному зверненні ми не будемо витрачати дорогоцінний час.

Після цього ми отримуємо Html контенту попап. Для цього скористаємося ф-цією jQuery clone (). Звичайно можна було піти трохи іншим способом і вставляти в нашу оболонку безпосередньо сам об'єкт, до якого застосували метод showPopup, а після закриття попап повертати його на місце. Але в такому випадку, якщо в попап є, наприклад, інпут - потрібно буде стежити за їх очищенням. Загалом мені здалося що це був би зайвий функціонал. Отже. Ми отримали html потрібного контенту для попап. Тепер вставляємо його в оболонку. Причому вставляємо ми його замінюючи весь Html, який був в оболонці. Це потрібно для того, щоб якщо раптом там був інший попап - не вивести заодно і його. Ну і методом show () замінюємо display: none оболонки на display: block.
Для того, щоб контент попап був «вище» всього іншого, йому потрібно прописати найбільший z-index. А так же нашим завданням є вирівнювання попап по центру. Для цього дамо йому значення left і top рівні 50%. Тепер верхній лівий кут попап знаходиться посередині сторінки. А для того, щоб по центру сторінки знаходився центр попап потрібно дати йому негативні margin-left і margin-top рівні половині його ширини і висоти відповідно. Винесемо цю логіку в окремий метод setAlign:

У цьому методі ми враховуємо і ті випадки, коли розмір попап більше розміру вікна. В цьому випадку, якщо ставити margin-left і margin-top рівні половині ширини і висоти попап помножені на -1 - частина попап просто сховається і не буде видна користувачеві. Тому ми перевіряємо, якщо половина ширини вікна браузера менше ніж половина ширини попап (лобо якщо простіше - ширина вікна менше ширини попап) - 'margin-left' буде дорівнює половині ширини вікна. Плюс робимо отбівочку в 10 пікселів щоб попап не прилипав до краю вікна.
Хотілося б повернутися до події кліка по попап, після якого попап закривається. Якщо залишити все як є - попап буде закриватися навіть тоді, коли користувач буде кликати по контенту попап. Для вирішення цієї проблеми я скористався ефектом спливання подій в jQuery. Як извесно, якщо у нас span лежить в div і юзер клікнув по span - спочатку спрацює подія у СПАНА, а потім відбудеться подія у дива. Для вирішення нашої проблеми ми просто запобігаємо спливання події методом stopPropagation ().
Залишилося тільки, як я і обіцяв, описати метод закриття попап. Він буде зовсім нескладним:

Метод ховає оболонку попап за допомогою методу hide (). Цим же методом він ховає фон попап. Потім ми повертаємо можливість скролла у body і даємо йому первісну ширину. І напишемо для нього внешнуюю функцію, яку можна викликати для закриття попап «вручну». Наприклад при натисканні на кнопку «Закрити попап»

Тепер, наприклад, у нас є блок

Все, що потрібно для того, щоб показати його в попап - викликати метод $ ( '# popup'). ShowPopup ();

вистачить вже говнокодіть (
Мені хочеться розстрілювати людей який ось так пхають html зі стилями всередину js. А потім знайти його гідним статті.


А якщо я колір фону поміняти хочу? І взагалі я за те щоб було мінімум зайвих обчислень на js і максимум грамотного css


якщо дуже грубо, всього 1 клас навісити. Вистачити марити, почніть писати вдумуючись в завдання.

Так як в різних ОС і різних браузерах ширина скролла може бути різна (десь вона взагалі дорівнює нулю), ми повинні її (ширину) визначити.
А навіщо нам її визначати?

Згоден з приводу css. На сайті, де використав цей попап, саме так і реалізовано. І html вставляєте за допомогою js-a, а відразу вставлений в body.
Але якби я приводив саме той приклад, довелося б писати - це всавте туди, ось це сюди ... Звичайно, можна було додати в коробку файл css. Але заради двох класів доведеться робити окремий запит на сервер. Але мене ще більше напружує, коли для того, щоб у тебе заробив плагін потрібно додати код Html, додати код в свій css, побігати навколо компа з бубном і виконати ще якісь маніпуляції ...

А якщо я колір фону поміняти хочу?
Природно всі ці значення можна винести в змінні.

Природно всі ці значення можна винести в змінні.
За це я б п *** мул ссанимі віником, чесне слово якщо у вас відображення задається js змінними - це фінішь. Бачити щось пообное в коді не хочеться:


Я ще раз повторю напевно: всі стилі в css, весь код js, вест лейаут в html.
html можна покласти в змінні js якщо передбачається, що плагін буде поставляти як готове рішення «тільки додай виклик».
Для великого проекту html краще винести в окремі файли шаблонів, тим більше, якщо як я сказав проект великий там вже є своя система шаблонів і система зручної роботи з ними.
Але заради двох класів доведеться робити окремий запит на сервер.
Нормальні люди підключають ці стилі до основних (руками або збирачем не важливо) ніяких запитів додаткових немає.