Pull to refresh

Боремся с неплавной анимацией скрытых элементов

Reading time 3 min
Views 25K
Работая с jQuery многие рано или поздно сталкиваются с одной особенностью: когда мы хотим показать с анимацией скрытый блок, его появление может быть дерганым. Происходит это не всегда, но довольно часто. К сожалению не все разработчики знают как с этим бороться, а те кто знают, зачастую ленятся. Явный пример дерганного появления блока, можно посмотреть прямо на главной странице jQuery, нажав на кнопку Run Code:

image

Отмечу, что глюк на данной конкретной странице можно увидеть во всех браузерах кроме ie6 и ie7. Итак, если нажать на кнопку Run Code, мы увидим плавное появление блока «p.neat» из прозрачного состояния с нулевой высотой и шириной примерно до такого состояния:

image

После чего высота блока резко скачет до раскрытого состояния:

image

Может даже показаться, что браузер на долю секунды «тормознул», но настоящяя причина такого поведения в том, что jQuery не верно считает высоту невидимого блока. Но не спешите стрелять в музыканта, давайте лучше попытаемся разобраться, почему так происходит. Отмечу заранее, с этим глюком можно справиться почти всегда.

Во первых, давайте обозначим некоторые условия появления глюка: Он возникает тогда, когда мы хотим «развернуть» ранее не видимый на странице блок с помощью функций show(speed), toggle(speed), slideDown() и animate({height: 'show'}). Очевидно, что для того, чтобы сделать анимацию раскрытия блока, нужно менять его высоту от нуля до какой-то высоты. Причем, эта высота должна быть вычислена до начала анимации. Как известно, HTML и CSS предоставляют нам только 2 способа спрятать блок, это свойства display: none и visibility: hidden. В первом случае элемент просто выключается, как будто его нет в исходном тексте страницы. При этом браузер не забивает себе голову такими вещами, как подсчет занимаемого блоком пространства и узнать высоту блока просто нельзя. В режиме visibility: hidden браузер делает вид, что блок существует, отводить для него место на странице, но просто не рисует его. Вот в этом режиме как раз можно, не показывая блок пользователю, узнать его размеры. Но просто спрятать блок от пользователя мало, нужно чтобы он все равно не занимал место на странице, и вот тут на сцену выходит свойство position: absolute, которое применяется к блоку перед получением его высоты. Различие высоты блока в конец анимации и настоящей его высотой, есть ни что иное, как различие высоты блока с position: static и position: absolute.

Если мы включим в браузере инспектор объектов и добавим к разворачиваемому блоку, свойство «style="display: block; position: absolute;"», мы увидим вот такую картину:

image

Именно до этой высоты jQuery и разворачивает блок. Что же, давайте попробуем пофиксить данный баг. Как известно, ширина элемента с position: absolute ограничена, если не задано другое, шириной ближайшего родителя с position ≠ static. В данном случае нам нужно, чтобы ширина элемента была как у прямого родителя, соответственно этому родителю и нужно задать position: relative. Снова берем в руки инспектор объектов, перезагружаем страницу, добавляем элементу «div.jq-codeDemo» свойство position: relative и жмем на кнопку Run Code. Ура! Мы только что с помощью одного свойства пофиксили анимацию на главной странице самого популярного JavaScript фреймворка :)

Конечно, можно только поблагодарить разработчиков сайта jQuery за то, что они сделали такой простой пример к моей статье. В реальной ситуации, возможно, придется сделать еще что-то. В общем случае, всегда нужно проверять появляющиеся блоки в инспекторе, меняя position со static на absolute и следить чтобы при этом не было изменений размера блока. Но можно выделить и более-менее определенные правила, которые нужно проверить до инспектора.
  1. Если возможно задать жесткий размер разворачиваемому блоку, задайте. И следующие правила можно не читать.
  2. Если размер должен быть в процентах от родительского блока, нужно убедиться, чтобы ближайший родитель с position ≠ static имеет тот же размер, что и непосредственный родитель объекта. Если это не так, можно поставить position: relative непосредственному родителю. При этом, если размер должен быть 100% от родителя, лучше указать это явно.
  3. У родителя с position ≠ static не должно быть горизонтальных паддингов. Это связано с тем, что для обычных элементов ширина блока считается от родителя без учета паддингов, а для элементов с position: absolute, с учетом.
  4. После начала анимации (т.е. после момента, когда jQuery посчитал высоту) нельзя изменять никакие свойства, которые могут повлиять на ширину или высоту родителя или анимированного объекта.


Собственно, на этом все, плавной вам анимации, друзья. Возможно, в следующий раз я расскажу вам о такой штуке, как отложенные события мыши и когда они бывают полезны.
Tags:
Hubs:
+72
Comments 11
Comments Comments 11

Articles