Pull to refresh

Кроссбраузерные проблемы псевдокласса :active

Reading time 4 min
Views 14K


С появлением множества нововведений в технологиях вёрстки веб страниц, у разработчиков появилась возможность отчасти заменить JavaScript, применяя HTML/CSS для большей производительности и расширяемости интерфейса своих порталов.
Помимо проблем с кроссбраузерностью и разной реализаций новых свойств CSS, часто приходится встречаться с другими проблемами в местах, где казалось бы, давно всё устаканилось и везде работает одинаково. Именно с такой проблемой мне пришлось столкнутся, применяя CSS transitions вместе с псевдоклассом :active. Видимо из-за того, что в документации отсутствует описание поведения родителей элемента в состоянии :active, в разных браузерных движках это поведение реализовано по-разному.

Задача


Кроссбраузерно декорировать потомка активного элемента (родителя), с возможностью активировать родителя кликом на любого потомка (мой пример на jsfiddle, и на dabblet).

Прототип


Учитывая потенциальные сложности и несостыковки поведения в разных браузерах, делаем рабочий прототип, для кроссбраузерной проверки.

span {
    display: block;
    position: relative;
    width: 60px;
    height: 60px;
    background: red;
    }

b {
    position: absolute;
    left: 10px;
    top: 10px;
    width: 40px;
    height: 40px;
    background: blue;
    }

i  {
    position: absolute;
    left: 10px;
    top: 10px;
    width: 20px;
    height: 20px;
    background: green;
    }

<span><b><i></i></b></span>


В сферическом вакууме


В спецификации указано, что активным элементом (прим. div:active), является тот, на который производится клик, но ничего не сказано как наследуется активное состояние. Делаем эксперимент, и смотрим, как же в реальном мире, ведут себя активные элементы, ссылка на страницу с экспериментом на Jsfiddle (на dabblet).

Первые проблемы


В первом случае, в webkit, FF 3.6+ и opera мы видим, что активное состояние наследуется всеми родителями, вплоть до <html>, но когда требуется отследить более 1ого активного элемента, опера уже не справляется, и активное состояние наследует только первый родитель в DOM, декорированный псевдоклассом :active (span:active)

html:active { background: #ededed; } /* Light gray */

.test1-2 span:active { background: yellow; }

<div class="test1-2"><span><b><i></i></b></span></div>



В Interner Explorer активное состояние вообще не наследуется, и работает только при клике на элемент декорированный :active псевдокласом (в эксперименте это <span>), а в версиях до 7ого, включительно, :active работает только на ссылки.



Это еще не всё


Во втором случае я использовал каскад от активного элемента (span:active b), как видим в примере, webkit и ff, всё работает как и предполагалось, кликая на любой элемент вниз по DOM от декорированного псевдоклассом стиля, подсвечивается нужный элемент, определённый через каскад. Опера в этом случае начинает вести себя как IE, переставая наследовать активное состояние и работая только с элементом, на которые непосредственно происходит клик.

html:active { background: #ededed; } /* Light gray */

.test2 span:active b { background: yellow; }

<div class="test2"><span><b><i></i></b></span></div>




Оружие к бою


В третьем примере удалось заставить оперу наследовать активное состояние от потомка для использования в каскаде, используя абсолютно позиционированный элемент (<em class=«pseudo»>), опирающийся от элемента декорированного псевдоклассом :active. FF и webkit тут по прежнему работают предсказуемо, но IE8+ до сих пор не при делах.

html:active { background: #ededed; } /* Light gray */

.test3 span:active b { background: yellow; }

em.pseudo {
    content:"";
    position: absolute;        
    top: 0; right: 0; bottom: 0; left: 0;
    }

<div class="test3"><span><b><i></i></b><em class="pseudo"></em></span></div>

Что бы заставить работать IE как нам требуется, и улучшить немного код, заменяем абсолютный элемент псевдоэлементом. Как выяснилось, в IE активное состояние всё таки наследуется, но только на псевдоэлементы ::before, ::after. К сожалению, в IE8 псевдоэлементы игнорируют z-index, что может не подойти в большинстве случаев (как и в моём), но в IE9+ всё в порядке.

html:active { background: #ededed; } /* Light gray */

.test4 span:active b { background: yellow; }

.test4 span:after {
    content:"";
    position: absolute;        
    top: 0; right: 0; bottom: 0; left: 0;
    }

<div class="test4"><span><b><i></i></b></span></div>​



Миссия закончена, возвращаемся домой


В итоге, мы заставили все элементы одинакого наследовать активное состояние кроссбраузерно (IE9+, FF 3.6+, Opera 9.64+, Chrome, Safari и другие вебкиты) и теперь спокойно можем реализовать функционал на портале.
Так как функционал несёт чисто декоративную роль, мы решил отказаться от IE6-8, оставив им мягкий fallback, а остальное сделать на CSS.

Постскриптум.


Без каскада


Можно проще решить проблему в IE8 и Опере, отказавшись от каскада, что в моём случае не подходило, и снижало расширяемость функционала, поэтому решил отказаться от реализуемой красивости в IE8.

Пояснение к задаче


Единственный нормальный способ отслеживать клик на CSS, в моём примере, без использования Java Script, оказался псевдокласс :active. Можно было использовать :focus, подставляя элементы формы под существующие блоки, но такое решение много тяжелее по нагрузке, и в итоге всё равно требовало Java Script напильника.

Тачпад


При клике с тачпэда элемент заметно меньше времени находится в активном состоянии, в отличии от клика с мыши.
Tags:
Hubs:
+37
Comments 19
Comments Comments 19

Articles