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.
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.
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.
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; };
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ą.
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.
Sesja się już dla mnie skończyła, można więc coś napisać. Dzisiaj będzie jednak krótko, bo i temat prosty.
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.
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 :).
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.
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ę?
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.
Uwaga: wyszedł mi "odrobinkę" przydługi wstęp. Zabieganych zapraszam od razu do konkretów.
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ć:
co to był za kod tego pomarańczowego, który używam do podkreśleń?
-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.
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.
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.
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 :).
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.
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; }
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? :)
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.