Почему комбинатор общего брата позволяет переключать содержимое псевдоэлемента, но не смежного брата?

В этом вопросе " Селектор CSS3, который работает как jcuery's.click ()? ", Я опубликовал ответ, используя :checked состояние input из type="checkbox" переключить отображение элемента.

Это HTML-код демонстрации, которую я разместил в этом ответе:

<input type="checkbox" id="switch" />
<nav>
    <h2>This would be the 'navigation' element.</h2>
</nav>
<label for="switch">Toggle navigation</label>

И CSS (с переходами для краткости):

#switch {
    display: none;
}
#switch + nav {
    height: 0;
    overflow: hidden;
    /* transitions followed */
}
#switch:checked + nav {
    height: 4em;
    color: #000;
    background-color: #ffa;
    /* transitions followed */
}

label {
    cursor: pointer;
}

JS Fiddle demo.

Как только я опубликовал ответ, мне пришло в голову, что мы также можем переключать текст label используется для запуска изменения состояния этого флажка, используя следующие селекторы (изменив label текст к 'навигации'):

label {
    display: inline-block;
    cursor: pointer;
}

#switch + nav + label::before {
    content: 'Show ';
}

#switch:checked + nav + label::before {
    content: 'Hide ';
}

Упрощенная / базовая демонстрация JS Fiddle.

Это не сработало, в то время как селектор соответствовал, в то время как input был в своем непроверенном состоянии (а label показал Show navigation), селектор не соответствует, когда состояние input изменилось. Обратите внимание, что переходы по-прежнему были nav элемент, а исходный селектор сопоставления указывает, что комбинатор соседнего брата совпал изначально. Приведенная выше ссылка показывает упрощенную демонстрацию нерабочих (в Chrome 27/Windows XP) селекторов.

Затем мне пришло в голову попробовать комбинатор общего брата, чтобы уменьшить селекторную цепь. что привело к следующему CSS (с переходами, снова сокращенными для краткости):

#switch:checked + nav {
    background-color: #ffa;
}

label {
    display: inline-block;
    cursor: pointer;
}

#switch ~ label::before {
    content: 'Show ';
}

#switch:checked ~ label::before {
    content: 'Hide ';
}

JS Fiddle demo.

К моему удивлению, это сработало (content из label изменилось в ответ на измененное состояние input).

Итак, вопрос: почему комбинатор общего родственного брата допускает обновление позднего родственного брата, в то время как комбинаторы цепных соседей (которые соответствуют элементам и структуре DOM) не позволяют?

Кроме того, похоже, что это работает в Firefox (21, в Windows XP); поэтому я думаю, что вопрос немного изменен, чтобы включить: это ошибка в Chrome/Webkit или ожидаемое поведение?

И, более того, кажется, что, хотя это ошибка в Chrome (спасибо @Boltclock), есть несколько нелепая анимация "ничего не делать", которая исправляет неработающую демонстрацию (хотя существуют и другие, возможно, лучшие, альтернативы, как у Скотта). ответ показывает)

body {
    -webkit-animation: bugfix infinite 1s;
}
@-webkit-keyframes bugfix {
    from {
        padding: 0;
    }
    to {
        padding: 0;
    }
}
#switch {
}
#switch + nav {
    -moz-transition: all 1s linear;
    -ms-transition: all 1s linear;
    -o-transition: all 1s linear;
    -webkit-transition: all 1s linear;
    transition: all 1s linear;
}
#switch:checked + nav {
    background-color: #ffa;
    -moz-transition: all 1s linear;
    -ms-transition: all 1s linear;
    -o-transition: all 1s linear;
    -webkit-transition: all 1s linear;
    transition: all 1s linear;
}
label {
    display: inline-block;
    cursor: pointer;
}
#switch + nav + label::before {
    content:'Show ';
}
#switch:checked + nav + label::before {
    content:'Hide ';
}

JS Fiddle demo.

Примечание: причина, по которой я обновляю вопрос с помощью этого "исправления" вместо того, чтобы публиковать его как ответ, заключается просто в том, что вопрос был не "как я могу это исправить?" но (в основном) "почему это не работает?"

2 ответа

Решение

Это давняя ошибка в браузерах WebKit, связанная с использованием определенных динамических псевдоклассов с комбинаторами следующего уровня. Это происходит независимо от того, применяете ли вы стили к самому элементу брата или к псевдоэлементу этого элемента брата.

Я не знаю, подавал ли кто-нибудь еще отчет об ошибке, но это довольно часто встречается на сайте:

Как ни странно, также сообщалось, что в Chrome были проблемы с общим комбинатором, но, как вы заметили, он работает в данном сценарии:

Так что или это было исправлено, или что-то еще запускает / запускает это.

Bug Work Around

По-видимому, некоторые действительные псевдоклассы, объединенные в цепочку, позволяют ему работать.

Эти работы (см. Скрипка № 1, Скрипка № 2, Скрипка № 3):

#switch:checked + nav:only-of-type + label::before
#switch:checked + nav:nth-of-type(1) + label::before
#switch:checked + nav:nth-child(2) + label::before

Это не так (см. Fiddle # 4):

#switch:checked + nav:not([class]) + label::before

Я попробовал другой :not() комбинации, ни одна из которых не позволила ему работать.

** Лучший выбор **

#switch:checked + nav:nth-child(n) + label::before
Другие вопросы по тегам