Podstawowe informacje o expressions
12 stycznia 2007

Expressions są rozszerzeniem Internet Explorera, dzięki któremu można wykonywać skrypty JS na zbiorze elementów. Ich działanie porównuje się do funkcji z Excela - gdy zawartość komórek się zmieni, formuła zwróci nowe dane. Microsoft zachwalał je jako przyjazne skryptowanie stron dla webmasterów, ale ja chciałym przybliżyć ich składnię i możliwości.
Teoretycznie można je dodawać do dokumentu zarówno w JS i CSS, ale tylko ten drugi sposób jest warty opisania. Tak bowiem aplikuję bardziej skomplikowane haki z poziomu arkusza stylów IE, kiedy wszystko inne zawodzi. Musimy jednak uważać na to co i jak robimy, bo expressions są w stanie łatwo zawiesić przeglądarkę.
Składnia i użycie w CSS
selektor {właściwość: expression(…pseudokod JS…);}
Parę zastrzeżeń do powyższego zapisu. Użytą właściwością CSS może być np.: left albo width (do których finalnie przypiszemy obliczoną wartość), ale wcale nie musi. W praktyce okazuje się, że wystarczy stworzyć dowolną nową i podpiąć w niej kod. Prawidłowy okaże się poniższy przykład:
p {width: expression(…);xp: expression(…);xp2: expression(…);}
Gdybyśmy byli zmuszeni wrzucić expressions do zwykłego arkusza CSS, polecam poprzedzić właściwość -ie - tak oznacza się rozszerzenia CSS (-moz, -o).
Z doświadczenia wiem, że nie należy eksperymentować z istniejącymi właściwościami CSS, jeśli po prostu musimy odpalić jakiś niezwiązany z nimi kod. Przykładowo tak wygląda w drzewie DOM przykład z góry:
Expression a JavaScript
Napisałem wyżej pseudokod JS - uczyniłem tak, ponieważ podczas pisania expressions jesteśmy ograniczeni zasadami składni CSS. Dodatkowo możemy wykorzystywać uproszczenia IE i dodatki MS. Oto te, które wychwyciłem:
- Nie możemy używać nawiasów klamrowych, możemy normalnych. Można korzystać z łamania linii oraz innych białych znaków w celu zwiększenia czytelności.
- Jest wyjątek od tej reguły, ale o tym później.
- Nie możemy stawiać średników, zamiast tego należy wykorzystać przecinki.
- Zmienne deklarujemy bez
var - Możemy odwoływać się do elementów z danym
idjak do zmiennych (a.innerHTML = 'coś'jeśli w DOM istnieje<div id="a"></div>)
Obiekty i metody
W expression często będziemy używać słowa kluczowego this. Zwraca ono referencję do aktualnie przetwarzanego elementu DOM. Przykładowo:
#sidebar a {width: expression(this.parentNode.offsetWidth - 100 + 'px');}
Metody pozwalające na spacerowanie po drzewie dokumentu:
parentNode- rodzic elementufirstChild- pierwszy potomek elementuchildNodes- zbiór potomków elementu,childNodes[3]zwróci czwartypreviousSibling- element poprzedzający (w IE spacje i tabulatory między tagami są wliczane dochildNodes, więc trzeba uważać co się pobiera)nextSibling- element następny (j/w)Bardziej szczegółowy opis sposobów odnajdywania elementów w DOM wraz z grafami - zerknij, jeśli masz małe pojęcie o czym mówię.
A teraz przykład z expression. Jak widać można też zmienić styl za pomocą takiej konstrukcji:
#nav li {-ie-xp: expression(this.parentNode.firstChild.style.color = '#13AEBB');}
Wykonywanie
Jakiekolwiek zdarzenie na dowolnym elemencie na stronie powoduje wykonanie expressions. Jest to bardzo przydatne - jeśli zamierzamy pobierać szerokość layoutu i sprawdzać go z predefiniowaną wartością, emulując min/max-width. Jest także dość szybkie, ze względu na wbudowanie IE w Windows zapewne. ;-)
Jednak z pewnością będziemy na pewnym etapie chcieć wykorzystać standardowe metody DOM, aby zrobić pseudo GC. Napisanie kawałka skryptu bez sprawdzania, czy dla danego elementu został już wykonany to śmierć IE. Modyfikacja dokumentu uruchamia ponowne parsowanie CSS - do DOM zostaje dodanych kilkadziesiąt kolejnych bloczków. Nieskończona pętla.

Zabezpieczyć się przed takim scenariuszem jest dość łatwo - dodajmy fikcyjny atrybut parsed (nazwa nie ma znaczenia) i sprawdzajmy jego wartość. Zwykłe warunki if w expressions nie działają - pozostaje skorzystać z wersji liniowej.
-ie-xp: expression(this.parsed ? 0 : (……,this.parsed = 1))
Po polsku - czy atrybut parsed aktualnie przetwarzanego elementu (this) ma wartość zero? Jeśli tak, to wykonaj kod w nawiasie po dwukropku. W którym to ustawiamy parsed na 1, aby nie powielić znowu tego samego.
Ok - jako, że wiemy już dość dużo, pokuśmy się o dodanie diva clearującego na końcu każdego postu na blogu.
div.entry {-ie-xp: expression(this.parsed ? 0 : (clearer = document.createElement('div'),clearer.style.clear = 'both',this.appendChild(clearer),this.parsed = 1));}
Mogliśmy także ustawić odpowiedni className i pozbyć się w ten sposób stylów liniowych (powiedzmy zgrupować z nowoczesnym div.entry::after), ale to tylko przykład.
Ograniczenia składni a eval

Na początku postu pisałem, że w expressions nie możemy używać nawiasów klamrowych i średników. Przez takie ograniczenia odpadają wszelkie konstrukcje wspomagające kodowanie - złożone warunki, pętle, bloki try. Niedawno jednak spróbowałem skorzystać z funkcji eval wykonującej kod JS zawarty w dowolnym ciągu tekstowym. A te już mogą zawierać niedozwolone znaki! :-)
Jedyne o czym trzeba pamiętać to skasowanie deklaracji zmiennych var. Możemy je umieścić przed eval. A tak wygląda pętla drukująca w akapicie cyfry od 0 do 9:
p {-ie-xp: expression(this.parsed ? 0 : (i = 0,eval('for (i = 0; i < 10; i++) { this.appendChild(document.createTextNode(i)) }'),this.parsed = 1));}
Po co nam 10 cyfr? Nie wiem, ale to tylko przykład jak potężne potrafią być expressions. Dzięki nim można roszerzyć implementację CSS Explorera i naprawić dziesiątki błędów - o czym przekonamy się w następnym poście. Tymczasem powodzenia w hakowaniu misia. ;-)



