Селекторы (сестринский, дочерний, атрибутов), border-spacing, :first-child, :before и :after в IE.

    В CSS 2.1 есть множество рекомендаций, не поддерживаемых одним из самых распространённых браузеров, IE6. Но иногда так хочется использовать возможности CSS на полную мощность. Например, использование дочерних, сестринских селекторов, селекторов атрибутов и т.п. могло бы упростить HTML (или даже серверные скрипты, например, при вычислении первого потомка, тогда как в CSS для таких случаев предусмотрен псевдокласс :first-child). Каким же образом можно заставить IE понимать CSS в таких случаях?

    Используя динамические стили в Internet Explorer можно реализовать множество отсутствующих возможностей CSS.
    Я подготовил тестовую страницу, на которой при помощи одноразовых expression эмулируются некоторые возможности CSS, не поддерживаемые в IE.
    • дочерний селектор эмулируется работой со свойством parentNode:
      .div-p { color: red; }
      * html .child-sel p {
        z-index: expression(
          runtimeStyle.zIndex = 1,
          "div" == parentNode.tagName.toLowerCase() ? (className = "div-p") : 0
        );
      }
    • сестринский селектор эмулируется работой со свойством previousSibling:
      .p-p { color: red; }
      * html .sibling-sel p {
        z-index: expression(
          runtimeStyle.zIndex = 1,
          previousSibling && previousSibling.tagName && "p" == previousSibling.tagName.toLowerCase() ? (className = "p-p") : 0
        );
      }

    • селектор атрибутов эмулируется проверкой определённого свойства объекта (например, самый распространённый случай, различение элементов input по атрибуту type):
      .type-text { width: 300px; }
      * html input {
        z-index: expression(
          runtimeStyle.zIndex = 1,
          type && "text" == type.toLowerCase() ? (className = "type-text") : 0
        );
      }

    • border-spacing эмулируется установкой атрибута cellspacing таблицы:
      table {
        z-index: expression(
          runtimeStyle.zIndex = 1,
          cellSpacing = 5
        );
      }

    • псевдоэлементы :before и :after реализуются с помощью изменения свойства innerHTML:
      blockquote p {
        z-index: expression(
          runtimeStyle.zIndex = 1,
          innerHTML = "«" + innerHTML + "»"
        );
      }

    • псевдокласс :first-child эмулируется проверкой равенства ссылки на элемент и первого потомка предка элемента:
      .first-child { color: red; }
      * html li {
        z-index: expression(
          runtimeStyle.zIndex = 1,
          this == parentNode.firstChild ? (className = "first-child") : 0
        );
      }


    Решение работает и в IE 5.x

    Update: устранил повторное присваивание className, когда менять класс не нужно. Например, для дочернего селектора теперь
    "div" == parentNode.tagName.toLowerCase() ? (className = "div-p") : 0
    вместо
    className = "div" == parentNode.tagName.toLowerCase() ? "div-p" : className
    AdBlock has stolen the banner, but banners are not teeth — they will be back

    More
    Ads

    Comments 41

      +1
      Спасибо.
        +1
        по моему, это очень интересное техническое решение

        но к сожалению, css-выражения будут исключены в режиме IE8 Standards и сами разработчики IE не рекомендуют их больше использовать
        поэтому лично я бы не стал использовать то что вы предложили в реальных проектах, даже для поддержки старых версий IE, которых нужно имхо не поддерживать, а устранять

        вообще, говоря о проблеме старых IE, разработчики почти никогда не хотят влиять на пользователя предложив ему сменить браузер, и по моему совершенно зря. Поддерживая всяческие недостатки разработчики тем самым косвенно поддерживают и сами устаревшие браузеры, которые до сих пор живы.
          +1
          надеюсь, в IE8 хаки для реализации данных решений не понадобятся
            0
            совершенно точно узнать что будет и чего не будет в IE8 (в плане CSS) можно по этой ссылке
            msdn.microsoft.com/en-us/library/cc351024(VS.85).aspx
              –1
              У майкрософт никогда ничего нельзя знать совершенно точно
            +5
            Большинство пользователей IE 6 физически не могут сменить браузер. У их учётной записи, как правило ограниченные права. Особенно хорошо это становится очевидно, если просмотреть статистику посещаемости какого-нибудь большого ресурса. В рабочие часы доля IE существенно выше, чем вечером и в выходные.
            • UFO just landed and posted this here
                0
                спасибо, исправил
                0
                В сторону пустые разговоры — ставьте бетку восьмого IE!
                  0
                  а причём тут собственно IE8? Ну запретят у них expression, но ведь IE8 понимает css2.1, зачем ему видеть css для IE6?
                  Если делать по уму то, вставки с Expressions будут видны только IE6 пользователям IE8 будет видеть то, что видят другие современные браузеры.
                  +1
                  А зачем там в каждом expression такая строчка:
                  runtimeStyle.zIndex = 1

                  Это какой-то очередной костыль?
                  Это полезно. Спасибо.
                    +4
                    для «выключения» expression (т.е. сначала в z-index содержится динамический css, после первой отработки превращаем в статический), иначе оно будет выполняться при каждом событии в браузере
                    +1
                    Спасибо за то, что собрали всё это в одном месте.

                    На мой взгляд, вместо z-index лучше использовать какое-нибудь проприетарное свойство, тот же zoom или scrollbar-base-color. Маловероятно, но z-index может быть использован по прямому назначению.

                    Кстати, вызов имени класса таким образом точно одноразовое?
                      0
                      Вызов класса одноразовый, но устранил повторное присваивание className (когда его не нужно менять).
                      Насчёт использования других свойств: z-index дан скорее в качестве примера, можно использовать другое свойство. В то же время упомянутые вами zoom и scrollbar-base-color имеют определённые ограничения: zoom не работает в IE5, а scrollbar-base-color применяется не ко всем элементам.
                        0
                        на практике scrollbar-свойства применяются ко всем элементам.
                        Это легко проверяется через IE Developer Toolbar включением default style values.

                        MSDN с одной строны говорит нам что «This property applies to elements that display a scroll bar» а с другой — «Applies To: APPLET, BDO, BODY, currentStyle, CUSTOM, defaults, DIV, EMBED, OBJECT, runtimeStyle, style, TEXTAREA», в любом случае на практике это работает в IE 5-7.

                        Ещё оч. хорошо что их много, хватает чтоб повесить на один элемент несколько expressions:
                        scrollbar-face-color
                        scrollbar-base-color
                        scrollbar-3dlight-color
                        scrollbar-arrow-color
                        scrollbar-highlight-color
                        scrollbar-shadow-color
                        scrollbar-track-color
                      +1
                      а вы знаете что ие иногда выдает ошибку и не догружает страницу если пытаетя изменить DOM до его загрузки. я говорю про :before и :after
                        0
                        и для примера лучше было бы использовать dl dt dd чем fieldset. имхо
                          0
                          Пожалуй, да. Изменил.
                          0
                          Конечно, я знаю об этой проблеме. Возникает, когда скрипт, содержащийся в элемента пытается менять структуру предка. В моём примере expression меняет тот же блок, в котором вызывается, т.е. ошибка возникать не должна.
                            0
                            когда у меня возникла такая проблема, я пользовался:

                            zoom:expression(
                            runtimeStyle.zoom = 1,
                            insertAdjacentHTML('afterBegin','… ххх...'),
                            insertAdjacentHTML('beforeEnd', '… ххх...')
                            )

                            и

                            scrollbar-face-color:expression(!this.isInserted==true? this.isInserted=(this.innerHTML = '...' + this.innerHTML + '...'): '');

                            и ошибка все равно возникала. я в этих экспрешенах не очень рублю, может они отличаются чем то важным от твоего примера. хотелось бы конечно раз и навсегда разобраться с возможностью применения innerHTML
                              0
                              к слову тогда заместо "..." я вставлял дивы, а не обычный текст
                                0
                                В документации по insertAdjacentHTML написано
                                You cannot insert text while the document is loading.
                                  0
                                  хотелось бы конечно раз и навсегда разобраться с возможностью применения innerHTML

                                  Рекомендации просты:
                                  — стараться воздерживаться от использования таких конструкций, особенно на крупный проектах;
                                  — не вставлять сложные элементы (желательно только текст), если всё-таки решили их применить.
                                    0
                                    если определять js-переменную в конце кода страницы, то expression будет срабатывать, но в некоторых случаях в IE6 всё равно будет высвечиваться js-ошибка.

                                    я решил эту проблему проверкой наличия элемента:
                                    scrollbar-face-color: expression(document.getElementById("bottom") ? (
                                    this.runtimeStyle.scrollbarFaceColor = "#fff",
                                      0
                                      тогда придется добавлять этот элемент в самый конец, что не очень хорошо
                                        0
                                        ну речь за уже существующий элемент.
                                        что-то вроде #bottom, #footer — всегда есть.
                                          0
                                          да есть, но может быть так что сам элемент уже есть, а то что в нем еще не загрузилось, и тогда опять operation aborted
                                0
                                а я first-child эмулировал вот так: div {: expression ( (runtimeStyle..parentNode.getElementsByTagName(tagName).uniqueID == uniqueID)? /* if first child*/: /* if not first child */; ); }
                                  0
                                  эх, handless…
                                  div {: expression ( (runtimeStyle.parentNode.getElementsByTagName(tagName).uniqueID == uniqueID)? /* if first child*/: /* if not first child */; ); }
                                    0
                                    Блин, хабр съедает символы :( ну думаю кому надо — поймут
                                  0
                                  псевдостиль :first-child

                                  :first-child — это псевдокласс
                                    0
                                    Спасибо, исправил
                                    +1
                                    Одноразовые expression это конечно хорошо, но в случае динамически-подгружаемого контента в ними начинается морока.
                                    • UFO just landed and posted this here
                                      • UFO just landed and posted this here
                                          0
                                          Забить на IE без скриптов.
                                          • UFO just landed and posted this here
                                              0
                                              MSIE пользуются «не гики», которые не отключают скрипты, картинки и т.п. Я так считаю.
                                          0
                                          лучше класс добавлять вот так (className += " type-text")
                                          мало ли, вдруг на элементе уже есть какой-нибудь класс, в вашем случае он полностью заменится
                                            0
                                            Кстати last-child эмулируется так же, как first-child, нужно лишь заменить соответствующие слова.

                                            А вообще, с этими экспрешенами есть пробелма: нельзя например использовать два таких экспрешена для одной таблицы (сначала чтоб получить 1ую строку, затем, чтоб все первые ячейки каждой строки). Сработает только первый.

                                            Мы написали скрипт, который сразу ищет все таблицы в заданной области и автоматически расставляет по соответствующим ячейкам и строкам классы «first-child» и «last-child», что дает в итоге удобное кроссбраузерное управление этими элементами. И не нужно тонной экспрешенов засорять ксс и хтмл максимально чистый. Приводить скрипт тут не буду — парсер наверняка порежет. Кому нужно — пишите в личку.
                                              0
                                              Про контент: innerHTML = "«" + innerHTML + "»" понятно с :before и :after.

                                              А как быть для IE7 стилями псевдоэлементов, в случае когда content=""?
                                              (на IE6 забил, в IE8 как я понимаю всё работает)

                                              Ну если не совсем понятен вопрос, то я использую :before и :after в основном для задания стилей мнимым элементам, а не только для их символьного содержания. Например, когда:
                                              .class:before {
                                              background: white;
                                              color: black;
                                              content: "";
                                              display: block;
                                              height: 10px;
                                              margin: 10px 0 0;
                                              width: 20px;
                                              }

                                              Эти фиксы тогда юзать?: code.google.com/p/ie7-js/

                                              Only users with full accounts can post comments. Log in, please.