The end – blog zamknięty

5 komentarzy

Myślałem, że dam radę prowadzić dwa blogi, jeden bardziej techniczny, drugi luźniejszy, czyli ten. Nic jednak z tego, dlatego zamykam tego bloga. Od roku wszystkie moje artykuły (głównie JavaScript) lądują na Code42.pl.

Dziękuję Joggerku, to były wspaniałe lata z Tobą :)

Artykuł został przeniesiony na mój nowy blog – code42.pl.

Artykuł został przeniesiony na mój nowy blog – code42.pl.

Artykuł został przeniesiony na mój nowy blog – code42.pl.

Artykuł został przeniesiony na mój nowy blog – code42.pl.

WTFJS - rozjaśnienie umysłu

7 komentarzy

Blog WTFJS o dziwactwach w JavaScriptcie widział już pewnie każdy. Niektóre przykłady są naprawdę fajne :) Co więcej — niektóre działają mi w sposób przedstawiony przez autorów tylko czasami, choć to pewnie nie problem JavaScriptu, a Firebuga :).

Jako, że lubię wiedzieć dlaczego tak to działa, a nie inaczej, staram się wyjaśnić sobie o co chodzi w każdym przykładzie. Niektórych nadal nie rozumiem, część jest oczywista, część ma rozwiązania podane na stronie, a te ciekawsze postaram się wyjaśnić w tym (i może następnych) wpisach.

Jestem prawie obiektowy

typeof "abc" == "string" // true
typeof String("abc") == "string" // true
String("abc") == "abc" // true -- same types get casted to equal each other
 
String("abc") instanceof String // false -- hmmm...
(new String("abc")) instanceof String // true
String("abc") == (new String("abc")) // true -- wait, wtf?

new String() to nie String(), a "string" to nie "object". Proste? :) Trochę wolniej:

Pierwsza linia jest oczywista, ale warta zapamiętania — typem literału stringa jest "string". W drugiej mogłoby się wydawać że tworzymy obiekt, ale nie — konstruktor String wywołany bez new po prostu rzutuje na stringa. Trzecia linia tylko to potwierdza.

Czwarta linia to nawiązanie do tytułu jaki nadałem tej sekcji — Jestem prawie obiektowy. Autor tego przykładu zdziwił się skąd takie zachowanie, ponieważ pewnie nie wiedział o typach prostych w JavaScriptcie. Otóż wartości: 1, "a", true nie są typu "object" tylko odpowiednio "number", "string", "boolean". Nie są z tego powodu instancjami swoich podobnie brzmiących z nazwy klas (ale uwaga — czerpią z ich prototypów). Jest to zachowanie całkiem uzasadnione (choć niefajne w działaniu), ponieważ w innym przypadku (gdyby typy proste nie istniały) nie działało by porównanie ===.

Wyobraźmy sobie sytuację, w której pisząc "a" tworzymy nowy obiekt typu "string". Porównanie dwóch zmiennych, które są obiektami za pomocą operatora === sprawdza, czy zmienne te odnoszą się do dokładnie tego samego obiektu w pamięci. Tak więc w naszej hipotetycznej sytuacji otrzymalibyśmy:

"a" === "a"; // -> false

Uups. Po lewej obiekt, po prawej obiekt, ale to nie te same obiekty. Na szczęście teraz jest tak:

"a" === "a"; // -> true
new String("a") === new String("a"); // -> false
true === true; // -> true
new Boolean(true) === new Boolean(true); // -> false

Nie twierdzę, że jestem fanem takiego rozwiązania. Nie jestem pewien, ale chyba w Javie jest podobnie, za to na przykład już w Rubym jest to rozwiązane lepiej (ktoś potwierdzi?). Cóż — nic nie jest idealne ;).

Wróćmy jednak do przykładu. W piątej linii wreszcie tworzymy obiekt typu String. Dla przypomnienia:

typeof new String("a"); // -> "object"
typeof String("a"); // -> "string"

Ostatnia linia powinna być już oczywista jeśli powiem, że operator == przeprowadza konwersję zmiennych stojących po obu jego stronach do jednego typu (którego, tego nie wiem — pewnie zmiennej po lewej — kto ma ochotę niech sprawdzi w specyfikacji i napisze w komentarzu :). Tak więc z porównania String("abc") == (new String("abc")) otrzymujemy porównanie "a" == "a". Dla porównania:

String("abc") === (new String("abc")); // -> false

Widać więc, że trzeba uważać na krótki operator porównania i stosować go z głową. Polecam przyzwyczajenie się do używania potrójnego operatora i tylko w szczególnych wypadkach, kiedy na przykład znamy typy zmiennych (typeof x == "string"), używanie podwójnego.

Jestem prawie obiektowy, ale umiem porównywać

Kolejny przykład będzie tylko rozwinięciem poprzedniej części. Oto on:

(1) === 1; // true
Number.prototype.isOne = function () { return this === 1; }
(1).isOne(); // false!
Number.prototype.reallyIsOne = function () { return this - 1 === 0; }
(1).reallyIsOne(); // true

Pierwsza linia powinna być oczywista — nawias nie ma znaczenia. Druga i trzecia to ścisłe nawiązanie do poprzedniej części. W isOne typeof this; // -> "object". Mimo, że wywołujemy metodę na zmiennej typu "number", to jest ona po cichu rzutowana na "object" — dziwne, ale do zrozumienia — ciężko żeby this nie było obiektem.

Dwie ostatnie linie, to dynamiczne rzutowanie. Operacja odejmowania stara się rzutować obie zmienne na typ liczbowy. this jest rzutowane na 1 i reszta jest jasna. Równie dobrze można byłoby zapisać reallyIsOne w ten sposób:

Number.prototype.reallyIsOne = function () { return +this === 1; };

Używaj średników!

Poprzedni przykład przekleiłem na czysto z WTFJS. Jeśli będziecie go w tej postaci wklejać do Firebuga, to możecie się spotkać z latającymi wyjątkami. Skąd ten problem? Na końcu drugiej i czwartej linii brakuje średników i przynajmniej firefoksowy silnik wysiada. Rada na przyszłość — średniki dobre są.

Niezrozumiałe dla mnie

Tego już nie wytłumaczę :). Choć — obstawiałbym, że problem leży gdzieś w standardzie reprezentacji liczb zmiennoprzecinkowych, ale to tylko mój domysł.

Number.MAX_VALUE*1.0000000000000001 === (1/0) // false
Number.MAX_VALUE*1.0000000000000002 === (1/0) // true

Artykuł został przeniesiony na mój nowy blog – code42.pl.

Artykuł został przeniesiony na mój nowy blog – code42.pl.

Dlaczego frameworki CSS ssą?

33 komentarze

Sesja się już dla mnie skończyła, można więc coś napisać. Dzisiaj będzie jednak krótko, bo i temat prosty.

Cóż to takiego framework CSS?

No właśnie. Każdy wie co to są frameworki programistyczne, ale CSSowy? Według wiki:

A CSS framework, also known as a web design framework is a pre-prepared library that is meant to allow for easier, more standards-compliant styling of a webpage using the Cascading Style Sheets language. Just like programming and scripting language libraries, CSS frameworks (usually packaged as external .css sheets inserted into the header) package a number of ready-made options for designing and outlaying a webpage.

Czyli, tak jak w językach programowania, framework jest biblioteką (w tym wypadku grupą reguł CSS) która ułatwia implementowanie jakichś standardowych, często powtarzających się funkcjonalności (w tym wypadku pewnych schematów w layoutcie).

Co, według mnie, frameworkiem CSS nie jest? Pisałem w listopadzie o parserze/kompilatorze/procesorze CSS - LESS, który rozszerza składnię CSS o kilka świetnych rzeczy. Między innymi - zmienne, zagnieżdżone reguły, obliczenia, wielokrotne wykorzystywanie reguł. Jakby nie było - nie podchodzi to pod definicję z Wikipedii, ale niestety znajduje się w linkach do przykładowych frameworków. Dla ustalenia uwagi - LESS nie jest frameworkiem.

Czemuż ssie?

Pod mą lupę wziąłem pierwsze linki zwrócone przez Google. Przyjrzałem się chwilkę frameworkom: Blueprint, Elastic CSS, YUI 2 Grids. Prawdopodobnie są lepsze rozwiązania od tych i z chęcią się im przyjrzę jeśli linki pojawią się w komciach :).

Słówko o nieudanym porodzie

Wypada jeszcze wtrącić, dla niezorientowanych, co to za poroniony pomysł ten Grid system. Otóż wpadł ktoś na pomysł, że wygodnie będzie wszystkim (grafikom, programistom) jak ustalimy sobie, że strona składa się z X kolumn po Ypx szerokości każda, między którymi są marginesy po Zpx. Przyznam, że obaj z grafikiem byliśmy zachwyceni, aż do... pierwszego projektu. Życzę np. sporo zabawy z cieniami wychodzącymi poza granice kolumn, w przypadku kiedy nie możemy użyć box-shadow. Szlag trafia wszystkie piękne, okrągłe wartości.

Koniec wtrącenia

Najpierw kawałek kodu z YUI 2 Grids:

<div id="yui-main">
   <div class="yui-b">
      <div class="yui-g">
         <div class="yui-g first">
            <div class="yui-u first"></div>
            <div class="yui-u"></div>
         </div>
         <div class="yui-g">
            <div class="yui-u first"></div>
            <div class="yui-u"></div>
         </div>
      </div>
   </div>
</div>

Tyle się kiedyś mówiło o semantyce kodu i oddzieleniu wyglądu od treści. Ponad pięć lat temu, kiedy zaczynałem naukę HTMLa i CSSa, trwała walka o to aby programiści zaczęli pisać porządny kod. Wydawało mi się, że teraz już nie powinno być z tym problemów, a tu zonk. Patrzę na ten przykład i co widzę?

  • Prezentacja zbita razem z HTMLem - żeby zmienić układ strony zmieniamy HTMLa, a nie CSS,
  • Kompletnie bezznaczeniowe nazwy klas i identyfikatorów - jedno wielkie semantyczne szambo. Wybrałem akurat framework od YUI, bo jest w tym najgorszy (choć Blueprint mu nie ustępuje). Elastic CSS wygląda trochę lepiej,
  • Divitis. W przypadku YUI można chyba akurat dowolnie zmieniać użyte tagi, bo framework resetuje marginesy i paddingi dla wszystkich elementów (co też, tak na marginesie, uważam za kiepską praktykę). Kiedy jednak używając Blueprinta postanowimy zawrzeć którąś kolumnę w listę, to wszystko szlag trafia, bo uwaga... niektóre selektory zawierają nazwę tagu :O
  • Narzucona struktura i kolejność elementów w kodzie HTML. Ja akurat poświęcam sporo uwagi temu aby każdy element nawigacyjny był w sensownym miejscu, aby nie używać niepotrzebnych tagów, a wykorzystując framework nie mam tej elastyczności,
  • I wreszcie - przecież to wszystko co oferuje framework można w czasie, który jest bez znaczenia w stosunku do całego projektu, napisać ręcznie. To jest kilka reguł, które doświadczona osoba pisze na raz i to od razu z ewentualnym hackiem dla IE6. Tak, wiem, że nie wszyscy mają taką wiedzę, ale wykorzystując framework nigdy jej nie pogłębią.

Tak więc w żadnym wypadku nie widzę sensu w używaniu frameworków do budowy układu strony. Żeby jednak nie było, że w ogóle nie umiem wykorzystywać zewnętrznego kodu - uważam, że przydatne są frameworki poprawiające typografię, bądź też pod niektórymi względami formularze. Muszą jednak bazować na selektorach używających tagów, a nie klas, czy nie daj Boże identyfikatorów.

Write LESS, do more - czyli pimp my CSS

31 komentarzy

Uwaga: wyszedł mi "odrobinkę" przydługi wstęp. Zabieganych zapraszam od razu do konkretów.

CSS rady nie daje

Nie da się ukryć że frontend developerzy (czy inaczej "ci od cięcia grafiki" :) łatwego życia nie mają. Pierwsza sprawa to niedoskonałości samych języków (CSS, xHTML, Javascript+DOM), druga to różnice i bugi (zwane inaczej ficzerami) w obsłudze przez przeglądarki. Każdy z tych języków na szczęście się rozwija (CSS3, HTML5, ECMAScript 5), a implementacja standardów jest, nawet w Internet Explorerze, sukcesywnie polepszana. Trwa to jednak strasznie długo, dlatego warto pokombinować samemu.

Ostatnio opisałem plugin do edytorów tekstów dzięki któremu można przyspieszyć pisanie HTMLa. Dzisiaj pod lupę chciałbym wziąć CSS. Osobiście na tempo pisania listy właściwości nie narzekam - mam własny zestaw snippetów, który umila mi życie. Inaczej ma się sprawa w stosunku do selektorów. Już w niedużym projekcie potrafią się zrobić cuda typu:

#content .news_list .item .title {...}
#content .news_list .item .date {...}
#content .news_list .item .date .day {...}

Albo:

.s1 { width:120px; float:left; font-size:1.1em; margin-left:10px; color:#FA0; }
/*gdzieś kawałek dalej:*/
.s2 { width:120px; float:left; font-size:1.1em; margin-left:10px; color:#C14; }

Gdzie wypada to połączyć do pary selektorów i dla każdego z osobna jeszcze kolor ustawić. Niestety to najprostszy przypadek z tej rodziny, czasami trzeba znaleźć części wspólne dla kilku reguł po kilkunaście właściwości, albo olać wszystko.

Dalej można wymieniać:

  • Powtarzające się wartości kolorów, rozmiarów, marginesów, itd. Zmiana jednej wymaga zmiany pozostałych, bo np. tekst ma mieć ten sam kolor w nagłówkach newsów co gdzieś w sidebarze i górnym menu. I jak pozapamiętywać te wszystkie kluczowe wartości, żeby później nie szukać co to był za kod tego pomarańczowego, który używam do podkreśleń?
  • Obliczenia - dopełnianie się marginesów, paddingów i borderów, kolor lekko ciemniejszy, kolor lekko jaśniejszy. Ja zawsze do cięcia siadałem z kartką A4, która już po kilku godzinach była zapisana do pełna wartościami i obliczeniami.
  • -moz-border-radius: 5px;
    -webkit-border-radius: 5px;
    border-radius: 5px;

Co więcej - to są problemy które pojawiają się już na etapie stron o średniej wielkości i kodowania w pojedynkę. W przypadku sporych serwisów (długo i wieloetapowo rozwijanych) i większego zespołu potrafią urosnąć do &*$#$%. To jest jak rzeźba w gównie. Trzeba przepisać cały ten bajzel. Sam ostatnio tak opisałem jeden commit do bazy.

Wysyp wspomagaczy

Jakiś czas temu dostałem od kogoś link do LESS CSS - parsera/kompilatora (nie wiem jak to nazwać) rozszerzającego możliwości CSSa o kilka interesujących rzeczy typu: zmienne, obliczenia, reguły zagnieżdżone, itd. O temacie jednak zapomniałem (mało ostatnio koduję) i przypomniał mi kilka dni temu o nim Occulkot, który podrzucił mi Sassa. Obydwa kompilatory napisane są w Rubym. Zrobiłem mały research i znalazłem jeszcze podobne cudo, tyle że w Pythonie - Clever CSS.

Po krótkim wczytywaniu się w przykłady zdecydowałem się na LESSa. Dodaje do CSSa trochę mniej ficzerów, ale przynajmniej nie zmienia jego składni (plik CSS jest poprawnym plikiem LESS) i nie wymusza na mnie jedynego, właściwego formatowania kodu (Sass i Clever CSS mają składnię czerpiącą z języków w jakich zostały napisane - odpowiednio Ruby i Python - wymagają więc wcięć).

Polecam przeczytać Haml-sass, czyli tragedia w dwóch aktach. Przy okazji tej lektury ja również odradzam Hamla, który jest w teorii cudownym (zen i te sprawy) systemem szablonów HTMLa. Tylko, że kompletnie to nieczytelne, brzydkie i bez sensu.

Instalacja

Po tym przydługim wstępie (chciałem usunąć, ale żal jak się już napisało) przejdźmy do konkretów.

Instalacja, dla posiadających gema (ci nieposiadający najlepiej niech go zainstalują), jest banalna:

$ sudo gem install less

Ewentualnie jeśli ktoś koduje w Railsach, to może go zainteresować ten plugin.Najprostszym sposobem skorzystania z LESSa jest jednak przy pomocy konsoli:

$ lessc style.less

Wykonanie tego polecenia spowoduje utworzenie pliku style.css zawierającego wynik działania kompilatora.

Ficzery

Zmienne

Kompilując taki kod:

@brand_color: #4D926F;
 
#header {
  color: @brand_color;
}
h2 {
  color: @brand_color;
}

Otrzymamy:

#header, h2 { color: #4d926f; }

Jak widać, poza podstawieniem zmiennej, LESS połączył też reguły o tej samej liście właściwości. To informacja dla miłośników oszczędzania na bajtach transferu :).

Wielokrotne wykorzystanie reguł

CSSowe DRY :).

.rounded_corners (@radius: 5px) {
  -moz-border-radius: @radius;
  -webkit-border-radius: @radius;
  border-radius: @radius;
}
 
h1 .stronger {
	font-weight:bold;
	font-size:1.1em;
}
 
#header {
  .rounded_corners;
  h1 .stronger;
}
 
#footer {
  .rounded_corners(10px);
}

Po kompilacji:

h1 .stronger {
  font-weight: bold;
  font-size: 1.1em;
}
#header {
  -moz-border-radius: 5px;
  -webkit-border-radius: 5px;
  border-radius: 5px;
  font-weight: bold;
  font-size: 1.1em;
}
#footer {
  -moz-border-radius: 10px;
  -webkit-border-radius: 10px;
  border-radius: 10px;
}
 

Przy czym definicja reguły musi wystąpić przed jej użyciem. Przykład z wykorzystaniem h1 .stronger nie ma uzasadnienia w praktyce (jest w zasadzie błędny), ale chciałem tylko pokazać, że można wykorzystywać ponownie dowolne reguły.

Zagnieżdżone reguły

To chyba mój ulubieniec. Dzięki temu rozszerzeniu unikniemy pisania co chwilę mega długich selektorów. Dziwię się w ogóle, że nie ma tego w oficjalnym CSSie.

#header {
  color: red;
  a {
    font-weight: bold;
    text-decoration: none;
  }
 
  > p {
	  color: blue;
  }
}

Po kompilacji:

#header { color: red; }
#header a {
  font-weight: bold;
  text-decoration: none;
}
#header > p { color: blue; }
Obliczenia

Chyba nie muszę tłumaczyć.

@the-border: 1px;
@base-color: #111;
 
#header {
  color: @base-color * 3;
  border-left: @the-border;
  border-right: @the-border * 2;
}
 
#footer { 
  color: (@base-color + #111) * 2; 
}

Po kompilacji:

#header {
  color: #333333;
  border-left: 1px;
  border-right: 2px;
}
#footer { color: #444444; }

To by było na tyle. Jeśli kogoś temat zainteresował to zapraszam do specyfikacji, gdzie autor omawia jeszcze kilka dodatków.

W każdym razie - cztery małe ficzery, a jak cieszą, prawda? :)

Praca z LESSem

Zastanawiałem się tylko jak usprawnić kompilację po wprowadzeniu zmian do pliku LESS. Najfajniej gdyby udało mi się w moim VIMie podłączyć wykonanie $ lessc pod operację zapisania, ale to nie na moje umiejętności. Mcv zaproponował mi takiego Makefile'a:

%.css: %.less
	lessc $<

Wykonujemy go uruchamiając: $ make plik1.css plik2.css. To już powinno mi się udać podpiąć w VIMie pod zapis pliku. Ewentualnie można spróbować tak:

all: *.css
 
%.css: %.less
	lessc $<

Co zadziała automatycznie dla wszystkich istniejących już plików CSS po wykonaniu prostego $ make. Przy czym, jeśli się mylę, to proszę mnie poprawić, bo nigdy żadnego makefile'a sam nie napisałem.

EDIT: w komentarzach pojawiły się rozwiązania mojego problemu.