[pl] Specificity, czyli co oznacza C w CSS

English version

CSS czyli kaskadowe arkusze stylów – coś, czego używa duża część frontend developerów. Jednak spotkałem się z programistami, którzy latami używali CSS, a wciąż myśleli, że style, które są na końcu arkusza nadpisują te na początku.

<p id="a" class="b">Some text </p>
<style>
.b{
color:red;
}
#a{
color:green;
}
p{
color:blue;
}
</style>

Kaskadowość to właśnie zasada które style nadpisują które. Na powyższym przykładzie mamy 3 razy zdefiniowane kolory ale ostatecznie Some Text będzie koloru zielonego.

Najważniejsze (to znaczy nadpisujące pozostałe) są style zawierające !important, przy czym użycie !important powinno się traktować jako ostateczność. Drugie w kolejności są style inline, to znaczy <p style=”color:red”>. Następne są właściwe arkusze stylów a najmniej ważne są domyślne style przeglądarki.

Jeśli zaś mamy kilka styli w arkuszu, to porównuje się ich specificity, i dopiero gdy specificity jest identyczne, to na samym końcu brana jest pod uwagę kolejność.

Specificity to punktacja jak dany selektor jest szczegółowy, z założeniem, że bardziej szczegółowe selektory nadpisują te bardziej ogólne. Składa się on z 3 liczb, ilości id, klas i typów.

Na przykład #a{color:green} ma specificity 1,0,0, bo posiada 1 id, 0 klas i 0 typów. Id w założeniu wskazuje konkretny element, dlatego style odwołujące się po ID nadpisują te bardziej ogólne

.b{color:red;} odwołuje się po klasie, więc ma specificity 0,1,0. Oprócz klas wliczane są tu też selektory atrybutów (np. [alt=””]) i pseudoklasy (np. :hover)

p {color:blue;} to selektor typu, więc ma specificity 0,0,1. Wliczają się tu również pseudoelementy np. ::before

Najbardziej ogólne selektory np. * mają specificity 0,0,0

Specificity sumuje się, więc np. .classA.classB.classC ma specificity 0,3,0. Sumuje się również, jeśli łączymy różne rodzaje selektorów np. html > body > .a.b.c ma specificity 0,3,2 (0 id, 3 klasy i 2 typy).

Uważać trzeba na :not(), gdyż on co prawda sam nie zwiększa specificity, ale selektory, które są w środku sumują się, tak więc div:not(#someID) ma specificity 1,0,1. Można też traktować to jako trik na podbicie specificity, stosując wewnątrz :not() id lub klasę, która nie istnieje :not(#dummyID).

Dopiero na samym końcu, gdy mamy kilka selektorów o tym samym specificity brana jest pod uwagę kolejność.

[en] Specificity: what means C in CSS

Wersja Polska

CSS – Cascading Style Sheets – somethink, that is used by big part of frontend developers. But even this, I met programmers, that have used CSS by years, and still have thinked, that style on the end of sheet overrides this on the beggining.

<p id="a" class="b">Some text </p>
<style>
.b{
color:red;
}
#a{
color:green;
}
p{
color:blue;
}
</style>

Cascading is set of rules which styles overrides which. On the example above we have a color defined 3 times, but at the end Some Text will be green.

The most importent (it means overriding others) are styles with !improtant, but using it should be treated as a last resort. Second are inline styles <p style=”color:red”>. Next are style sheets, and on the end default browser styles.

When there are multiple styles in style sheet, there are compared by specificity, and only, when specificities are equat, an order matters.

A specificity is a scoring, how each selector is specific, with an assumption, that more specific selector overrides thes more general. It is compound with 3 numbers: count of IDs, classes and types.

For example, #a{color:green} has a specificity 1,0,0, becouse it has got one id, 0 classes and 0 types. An ID is meat to indicate one element, so styles with ID overrides these without it.

.b{color:red;} is a class selector, so it has a specificity 0,1,0. Also attribute selectors (for example [alt=””]) and pseudoclasses (for evample :hover) are treated as equal to classes.

p {color:blue;} is a type selector, so it has a specificity 0,0,1. Also pseudoelements (for example ::before) are treated as types.

The most general selectors like * has a specificity 0,0,0.

A specificity as added, so for example .classA.classB.classC has specificity 0,3,0. Also when we join multiple kinds of selector specificity is added, like in html > body > .a.b.c we have a specificity 0,3,2 (0 ids, 3 classes and 2 types).

You should be carrefould with :not(), becouse it not changes specificity by itself, but selector inside are added, so div:not(#someID) has a specificity 1,0,1. It can be also treated as trick how to bust a specificity, by simpli using ID or class that doesn’t exist :not(#dummyID).

Only on the end, when we have more than one selector with the same specificity, an order in code matters.

[en]About size of code

Polish version

Good code is short code. What I mean is that if you can implement the same feature in 500 or 100 lines of code, 100 will be better. Main reason is shorter = containing less elements = less complex = easy to understand. Situation, when programmer don’t know what his code do is not funny. Also less code is less probability of making bug.

But some people use approach, that only seemingly reduces amount of code.

Libraries

If you use library instead of writing code yourself, you have less your’s code. But code inside library is still code, and it runs in the same way that code written by You. Moreover, publicly available libraries are adjusted to many use cases and contains lot’s of features You don’t need.

Don’t get me wrong, I’m not saying, that using libraries is wrong. I mean more situation, when I have working 20-30 line function, and other developer want me to delete it and use external library.

But library with big community has less chance of having bug.

Yes, but there is risk, that lib would do something you aren’t aware off, which can cause bug.

Couple years ago I worked with project that had used jQuery. Sometimes happened situation, that after clicking some button exception inside jQuery’s code was thrown, but after F5 problem disappeared. I spend lots of time to find cause.

Problem was, that in some other view I added event listener, but instead of handler method there was undefined. JQuery didn’t get any problems with this, but throwed exception while handling event, even if I switched view and this event handler wasn’t necessary.

But library has better quality of code

Maybe yes, maybe not. You need to remember, that not only ninja senior developers publish to github, so not everything there is good quality.

Also you need to remember, that main reason for making software is to work correctly. Good code quality is a means, not an end.

Microservices

Personally I think, that microservices architecture is only for the most experienced developers. I, having over 5 years of commercial experience, still feels too small for microservice.

Temptation for using microservices can be, that insead big monolith with lots of code you have small projects, that are simple to understand. Problem is, that one microservice is not a whole program. Even if there are separated, maybe on other servers, maybe written in deferent programming languages, only set of services makes something useful.

And in microservices you need to add logic for handling sending requests to other services, which always will be more complicated, that calling method in other class.

I’m not against microservices, but if you want to use them, you should have better reason why. If you want to keep order in code, split in to classes, namespaces, maybe libraries, but run it inside one process.

[pl]O wielkości kodu

English version

Dobry kod to krótki kod. To znaczy, jeśli możemy zaimplementować tą samą funkcjonalność w 100 liniach kodu, albo w 500, to wersja 100 linii będzie lepsza. Głównie dlatego, że krótsza = zawierająca mniej elementów = mniej złożona = prostsza do zrozumienia. A sytuacja, gdy programista nie wie co się dzieje w kodzie nie jest fajna. No i mniej kodu to mniejsza szansa na buga.

Ale niektórzy stosują podejście, które tylko pozornie zmniejsza ilość kodu.

Biblioteki

Jeśli zamiast napisać fragment kodu samodzielnie użyjesz biblioteki, masz mniej swojego kodu. Jednak kod wewnątrz biblioteki, to też jest kod, i uruchamia się on tak samo, jak kod napisany przez ciebie osobiście. Co więcej, tworząc publicznie dostępną bibliotekę autor musi dostosować ją do wielu różnych przypadków użycia i zaimplementować wiele funkcjonalności, których ty nie potrzebujesz.

Nie zrozumcie mnie źle, nie twierdzę, że używanie w kodzie zewnętrznych jest złe. Bardziej chodzi mi o sytuację, gdy mam działającą funkcję o długości 20-30 linijek, a inny developer każe mi ją skasować, a zamiast niej załadować zewnętrzną bibliotekę.

Ale biblioteka z dużym community ma mniejsze szanse, że w środku będzie bug.

Tak, ale za to pojawia się ryzyko, że biblioteka będzie robić coś, czego nie jesteś świadomy i to może doprowadzić do błędu.

Brałem udział kiedyś w jednym projekcie z użyciem JQuery. Czasami zdążała się taka sytuacja, że kliknięcie w jakiś przycisk wywoływało Exception wewnątrz samego jQuery, ale po odświeżeniu F5 problem znikał. Dużo czasu poświęciłem na znalezienie przyczyny.

Okazało się, że w innym widoku dodawałem event listener, ale zamiast metody handlera ustawiałem undefined. JQuery nie robił problemów z ustawieniem takiego listenera, ale rzucał exceptiona przy próbie obsłużenia, nawet jeśli byłem już na innym widoku i tamten event nie był mi potrzebny.

Ale biblioteka będzie lepiej napisana

Może tak, może nie. Trzeba pamiętać, że nie tylko senior ninja developerzy wrzucają projekty na githuba, więc to, że tam coś jest, nie znaczy od razu, że będzie lepsze.

Druga sprawa, że głównym celem tworzenia software jest by działał i robił to, co ma robić. To czy jest dobrze napisane jest tylko środkiem do osiągniecia celu, a nie celem samym w sobie.

Mikroserwisy

Osobiście jestem zdania, że mikroserwisy to architektura tylko dla najbardziej doświadczonych. Ja, mając ponad 5 lat doświadczenia komercyjnego, ciągle czuję się za mały do mikroserwisów.

Pokusą do użycia mikroserwisów może być, że zamiast dużego monolitu w którym jest dużo kodu, masz małe projekciki, które są krótkie i proste do zrozumienia. Problem polega na tym, że jeden mikroserwis to nie jest cały program. Co z tego, że będą oddzielone, na różnych serwerach, może w różnych językach programowania. Dopiero zbiór różnych serwisów tworzy coś użytecznego.

Za to dochodzi ci logika związana z komunikacją. Wysłanie requestu do innego serwisu będzie zawsze bardziej skomplikowane, niż zwykłe wywołanie metody w innej klasie.

Nie jestem przeciwnikiem mikroserwisów, ale jeśli chcesz je zastosować, powinieneś mieć lepszy powód. Jeśli chcesz tylko zachować porządek w kodzie, to podziel go sobie na klasy, namespace, może wydziel do osobnych bibliotek, ale uruchamiaj w ramach jednego procesu.

[en] Slow, bad and expensive – how not to make software

Polish version

In today’s post I want to talk about topic, that is not 100% technical, so I encourage to read it not only by programmers.

When you start doing a new project, you want to do it quickly. After all, time is money. But not everyone knows how to do project quickly.

How NOT to do

When I was a beginner in programming everything was clear and simple: I start making a first feature, I try to do it as quickly as I can, and then start a next feature. I don’t waste time for improve something, that works. I don’t waste time for keeping a clean code.

With this mindset at the beginning of project a progress in work is very noticeable. But problem is, that every new features is somewhat connected with these, that was written previously. If we have left a mess in code, now we need to work in this mess.

We add new features, project grows, it is more and more complicated. We start to get lost. It is easy to remember what code written yesterday do, but after couple of months you start to forget. We spent more and more time to understanding code written earlier. It happens more and more, that while we are making new feature, we broke old one (because we aren’t aware how some thinks works).

At the beginning of project work progresses fast, but after this it only slows down.

How to do

1st: thinking about future

Programmers must know about project everything they can: what it is supposed to do, how it will be used, what features are foreseen for future upgrowth. While writing code, programmers should think about what will write in future.

But you could ask: is this against an Agile? I anwear: no. An Agile implies, that reacting on changes is more important, that sticking to the plan, but it doesn’t imply, that you should work without any plan. Working with Agile you shouldn’t think about how software will work, but why.

2nd: no hurry

Developers shouldn’t be hustled. Hurry can speed up current part of project, but will make you problems in the long run. It even has it’s own name: technical debt – because it is like taking a load: now you have more money (working features), but in future you will need to pay interests.

But obviously there is a problem: how can client (that is not a programmer) can see, if developer work slowly because he work carefully, or if he is lazy? It is even bigger problem, when client pays to a software house hourly rate (because in this scenario faster = cheaper). In this scenario client could supervise developers, which implies hustling, what makes whole project made slower, so more expensive. Alternative approach is to trust software house, but then there is risk, that workers will spend payd hours on facebook (this is my personal experience, and I have to mention, that although I tried to be honest in registering work hours, it is hard to focus when I see on screen next to me scrolling of memes) and in worse scenario cheating in calculating work-hours (I worked in company that do that).

Personally I don’t see better solution that to avoid contracts, in which client pays developer for hours.

3rd: refactoring

You need to carry about quality of source code, by making refactorization on a regular basis. What can be seen as a waste of time, in big projects it is really a good idea. And i want to note words “on a regular basis”. If you start refactorization after years, you can find problems, that are no longer solvable.

4th: experience

To make big project you need experienced programmer (at least one in team). Freshman (junior developer) even if want to write clean code prepared for future development, will not know how to do this.

Beginner programmer can quickly learn how to do something, that work (he always has access to uncle Google), but only an experienced one knows how not to make troubles for future (if client/boss allows it – see previous points).

Summary

When I write this text I have over 5 years in commercial programming experience (I don’t include hobbyist programing). When I have been working, I allways tried to think how to optimize my work, bot most of people I met don’t think this way. Most of people assume, that hour of work of developer is hour of work of developer and that’s about it.

The longer I work in the industry, the more I realize, how bad this approach is. In this moment I work in project, which already took 15 000 work-hours. I think, it could take 1500-2000 work-hours, if it was done as it should be from the beginning. But in this moment there is nothing you can do to stop wasting time and money, other that leaving project.

So before starting next big project, think about organization of work, to avoid wasting tens of thousands dollars.

[pl] Źle, drogo i powoli – jak nie tworzyć oprogramowania

English version

W dzisiejszym wpisie chciałbym poruszyć temat mniej techniczny, dlatego zachęcam do przeczytania go nie tylko programistom. 

Gdy zaczynamy robić nowy projekt, chcemy zrobić go szybko. To zrozumiałe, bo w końcu czas to pieniądz. Tylko nie każdy zdaje sobie sprawę z tego, jak to osiągnąć, by projekt zrobić szybko. 

Jak tego nie robić 

Gdy byłem początkującym programistą sprawa wydawała się prosta: biorę się za pierwszą funkcjonalność z listy, robię ją możliwie jak najszybciej, a gdy skończę, to biorę się za następną. Nie marnuję czasu na poprawianie czegoś co działa. Nie marnuję czasu na dbanie o porządek w kodzie. 

Z takim podejściem na początku projektu praca idzie mocno do przodu. Problem jest taki, że kolejne funkcjonalności, które dodajemy, nie wiszą w próżni. Każdy nowy feature łączy się z tymi, które były napisane wcześniej. Jeśli we wcześniejszym kodzie zostawiliśmy bałagan, to teraz w tym bałaganie trzeba się jakoś odnaleźć. 

Dodajemy kolejne funkcjonalności, projekt rośnie, robi się bardziej skomplikowany. Zaczynamy się w nim gubić. Łatwo jest sobie przypomnieć co robi kod napisany wczoraj, ale po kilku miesiącach zaczynamy zapominać. Coraz więcej czasu schodzi na to, żeby połapać się w tym, co już jest napisane. Coraz częściej zdarza się, że robiąc nową funkcjonalność, psujemy już istniejącą (bo nie jesteśmy świadomi jak niektóre rzeczy tam działają). 

Na samym początku praca nad projektem idzie szybko, a potem tylko zwalnia. 

Jak to robić 

Po pierwsze – myślenie w przyszłość 

Programiści muszą wiedzieć o projekcie jak najwięcej: do czego ma służyć, jak będzie używany, jakie funkcjonalności są przewidziane na przyszłość. Chodzi o to, żeby pisząc kod myśleć już o tym co będzie w przyszłości. 

I teraz może pojawić się pytanie; czy to nie przeczy zasadom Agile? Otóż nie. Agile zakłada, że reagowanie na zmiany jest ważniejsze niż trzymanie się kurczowo planu, ale nie oznacza to, że w ogóle nie powinno być żadnego planu. Tyle tylko, że programując zwinnie nie skupiamy się na tym jak ma działać software, ale raczej po co

Po drugie – brak pośpiechu 

Programistów nie można poganiać. Pośpiech może przyśpieszyć dokończenie aktualnego fragmentu pracy, ale odbije się czkawką w przyszłości. Fachowo określa się to mianem długu technicznego – bo to działa jak zaciąganie kredytu: teraz mamy więcej pieniędzy (działających funkcjonalność) ale w przyszłości będzie trzeba zapłacić odsetki. 

Oczywiście tutaj pojawia się problem: jak klient, który nie jest programistą, ma rozróżnić, czy programista pracuje powoli, bo pracuje dokładnie, czy po prostu się obija? Jest to szczególnie problematyczne, jeśli klient płaci software house’owi za godzinę pracy programisty (bo wtedy zrobić szybciej = zrobić taniej). W takiej sytuacji klient może albo pilnować programistów – co sprowadza się do wyżej wspomnianego poganiania, co sprawi, że projekt jako całość zostanie zrobiony wolniej, więc klient za całość zapłaci więcej. Może też zaufać firmie programistycznej, narażając się na to, że programiści za jego pieniądze będą przeglądać wykop.pl czy inny joemonster (mówię to z własnego doświadczenia, i muszę dodać, że choć sam starałem się być uczciwy w logowaniu godzin, ciężko się skupić, gdy kątem oka na monitorze innego programisty widzisz skrolowanie memów) a w gorszym wypadku kantowanie w obliczaniu ilości godzin (również w takiej firmie pracowałem).  

Osobiście nie widzę tutaj lepszego wyjścia niż po prostu unikać umów, w których klient płaci programistą za godzinę. 

Po trzecie – refaktoring 

O jakość kodu trzeba dbać, robiąc na bieżąco refaktoryzację. Coś co może wydawać się startą czasu, tak naprawdę w dużych projektach się zwraca. Przy czym podkreślam słowo: na bieżąco.  Jeśli weźmiemy się za refactoring po latach może się okazać, że niektórych rzeczy już poprawić się nic da. 

I wreszcie po czwarte – doświadczenie 

Do robienia dużych projektów potrzebny jest doświadczony programista (przynajmniej jeden w zespole). Świerzak (czyli fachowo mówiąc junior developer) nawet jeśli będzie chciał pisać czysty, dobry kod przygotowany na przyszłe zmiany, to nie będzie wiedział jak. 

Początkujący programista raczej szybko nauczy się, jak zrobić, by coś działało (zawsze mamy wujka Google), ale dopiero doświadczony będzie wiedział, jak zrobić to tak, by nie zakopać się w przyszłości. Oczywiście o ile przełożeni/klient mu na to pozwolą (patrz poprzednie trzy punkty). 

Podsumowanie 

Gdy piszę ten tekst mam za sobą ponad 5 lat, przez które zawodowo zajmuję się programowaniem (programowania amatorskiego w to nie wliczam). Zawsze pracując starałem się myśleć o tym, jak moją pracę mogę zoptymalizować, aby zrobić więcej w krótszym czasie, jednak niewielu ludzi, z którymi współpracowałem, myśli w podobny sposób. Większość ludzi wychodzi z założenia, że godzina pracy programisty to godzina pracy programisty i tyle w temacie. 

Nim dłużej pracuję w branży, tym bardziej uświadamiam sobie, jak bardzo jest to złe podejście. W tym momencie pracuję w projekcie, który trwa już około 15 000 roboczogodzin. Moim zdaniem mógłby trwać 1 500h, max 2 000h, gdyby tylko od początku ktoś wziął się za niego porządnie. Tylko że w tym momencie rozwoju projektu nie da się już nic zrobić, by przestać marnować czas i pieniądze (nie licząc porzucenia projektu). 

Więc zanim zaczniesz robić kolejny duży projekt pomyśl trochę nad sposobem pracy, aby nie marnować dziesiątek jak nie setek tysięcy złotych bezsensownie. 

[pl] HTML vs XHTML – czym to się różni

English version

Dużo ludzi pisze kod w HTML, ale niewielu zgłębia ten temat. Jeśli nie wiesz, czym różni się <br> od <br/>, to ten artykuł jest dla ciebie.

Co było przed internetem?

SGML (Standard Generalized Markup Language) jest to format zapisu dokumentów tekstowych, w których oprócz samego tekstu są jeszcze dodatkowe informacje (tagi zwane też znacznikami). SGML stał się standardem ISO w 1986. Dokument SGML wygląda na przykład tak:

<tag1>
    Some Text
    <tag2 attribute otherattribute="value">
</tag1>

Standard ten definiuje składnię (ang. syntax) to jak dany dokument sparsować. Parser SGMLa wie, że mam tag o nazwie tag1 i że ma on dwoje dzieci: fragment tekstu i kolejny tag.

Ale standard nie definiuje co oznacza tag1 (tak zwana semantyka). Jest to język ogólnego przeznaczenia, stworzony jako baza dla innych języków jak na przykład

HTML

Upubliczniony w 1991 roku, HTML definiuje na przykład że <a> to link, a jego atrybut href zawiera adres url.

Poza HTMLem, sam SGML nie zdobył wielkiej popularności, dlatego niewiele ludzi nawet zna tą nazwę, ale osobiście uważam, że ważne jest by znać różnicę między składnią a semantyką.

XML i XHTML

W 1998 roku ukńczono pracę nad nowym standardem: Extensible Markup Language (XML). Jest to następca SGML’a, im tak samo jak on, zajmuje się tylko składnią dokumentu.

W 2000 roku W3C (organizacja standaryzująca) opublikowała specyfikację XHTML 1.0. Był to HTML 4.01, w którym składnia została podmieniona na XML. Oznacza to, że dokument XHTML jest 100% poprawnym dokumentem XML.

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
 <head>
   <title>XHTML 1.0 Example</title>
 </head>
 <body>
   <p>
     This is an example
     <br/>
     of XHTML
   </p>
   <div id="empty"/>
 </body>
</html>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 strictl//EN"
  "http://www.w3.org/TR/html4/strict.dtd">
<html lang="en">
 <head>
   <title>XHTML 1.0 Example</title>
   <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
 </head>
 <body>
   <p>
     This is an example
     <br>
     of HTML
   </p>
   <div id="empty"></div>
 </body>
</html>

Oba dokumenty wyglądają dosyć podobnie, dlatego też wielu programistów nie zwraca na te szczegóły uwagi.

Pierwsza duża różnica jest taka, że XML nie toleruje błędów składni. SGML w sytuacji niepoprawnego zapisu spróbuje sparsować tyle ile jest w stanie, XML po prostu zwróci syntax error.

Każdy dokument XHTML zaczyna się od deklaracji XML, w której jest zapisana wersja samego XML’a oraz kodowanie znaków.

XML posiada mechanizm przestrzeni nazw (namespace), dzięki temu można mieszać różne formaty w jednym pliku. Na przykład możesz umieścić fragment XHTML’a wewnątrz pliku SVG albo RSS

Samozamykające się tagi

HTML posiada mechanizm automatycznego zamykania tagów. Na przykład tag <br> zawsze sam się zamyka i nie może mieć żanej treści. Inny przykład: tag <p> nie może zawierać w środku innego tagu <p>, więc gdy w kodzie pojawi się

<p>first
<p>second
<p>last</p>

paser wie, że otwarcie nowego <p> powinno automatycznie zamknąć wcześniejsze. Przy czym jak łatwo zauważyć, wymaga to,  by parser znał indywidualne zachowanie każdego tagu. W przypadku XHTML musisz zamykać wszystkie tagi ręcznie, tak aby każdy parser XML powinien przetworzyć plik poprawnie bez znajomości tych tagów. jeśli czegoś nie domkniesz, dostaniesz syntax error.

Ale żeby kod był bardziej czytelny, w XML istnieje skrócony zapis <br/>, który jest równoznaczny <br></br>. Ale ważne, by pamiętać, że w zwykłym HTML znak / na końcu tagu jest ignorowany (jak cześniej wspomniałem, HTML stara się ignorować błędy) i nic nie robi.

Więc jeśli w kodzie napiszesz:

<body>
  <div/>
  <p>Some text</p>
</body>

to jeżeli test to fragment XHTML’a, zostanie to potraktowane jako:

<body>
  <div></div>
  <p>Some text</p>
</body>

a wewnątrz HTML’a jako:

<body>
  <div>
    <p>Some text</p>
  </div>
</body>

Porzucony XHTML 2.0

Na samym początku XHTML 1.0 i 1.1 miały 2 duże problemy. Pierwszy: nie był wspierany przez Internet Explorera aż do wersji 9 (czyli roku 2011, 11 po wprowadzeniu standardu), a trzeba pamiętać, że w tamtych czasach od wydania nowej wersji, do momentu, gdy znaczna część użytkowników zaktualizowała przeglądarki potrafiły minąć kolejne lata. W takich warunkach nawet pisząc XHTML, pliki trzeba było serwować z content-type: text/html, tak aby IE sparsowalo to jako HTML, tym samym tracąc wszystkie zalety XML’a.

Drugi problem: programistom nie spodobała się idea, że syntax error spowoduje, że cała strona/aplikacja przestanie działać, a użytkownik zobaczy wielki komunikat o błędzie.

W3C pracowało nad specyfikacją XHTML 2.0, który miał zrywać z wsteczną kompatybilnością. Ten pomysł niezbyt podobał się developerom, więc w 2006 roku pomysł został porzucony. Ale wbrew temu, co niektórzy myślą, nie jest to koniec historii XHTML’a.

Teraźniejszość – HTML5

Po tym, jak W3C porzuciło XHTML 2.0, stworzono nowy standard – HTML 5. Jednym z głównych założeń było to, że HTML i XHTML będą rozwijane równolegle w ramach jednej specyfikacji. programista może wybrać, którą składnię woli, ale od momentu sparsowania przez przeglądarkę reszta jest już identyczna.

W3C zdecydowało, że HTML nie będzie już dalej pełni kompatybilne z SGML (bo komu to potrzebne). Wprowadzono też ficzery dotychczas znane tylko z XHTML’a, np. osadzanie MathML (wciąż nie wspierane przez Chrome 😡) czy SVG wewnątrz HTML’a.

Warto zauważyć, że nawet jeśli nie wpisujesz przestrzeni nazw (namespace) w kodzie, przeglądarka sama doda je w trakcie parsowania. Łatwo to sprawdzić z poziomu JavaScriptu:

var div = document.createElement('div');
div.innerHTML = '<p></p><svg></svg>';
console.log(div.children[0].namespaceURI);// -> "http://www.w3.org/1999/xhtml"
console.log(div.children[1].namespaceURI);// -> "http://www.w3.org/2000/svg"

Trzeba pamiętać, że gdy chcemy dynamicznie tworzyć elementy przez obiekty DOM musimy samemu podać przestrzenie nazw, inaczej tag nie zostanie potraktowany jako SVG, ale jako nieznany tag HTML, gdyż jak wspomniałem wyżej, po sparsowaniu przeglądarkarka traktuje HTML i XHTML identycznie (z wyjątkiem pola innerHTML, ale to wyjątek).

var badSvg=document.createElement("svg");
console.log(badSvg.namespaceURI);// -> "http://www.w3.org/1999/xhtml"
var goodSvg=document.createElementNS("http://www.w3.org/2000/svg","svg");
console.log(goodSvg.namespaceURI);// -> "http://www.w3.org/2000/svg"

[pl][video] Kurs technologii WebGL – #1 co to jest WebGL

Cześć, witam cię na kanale The Bugger, ja jestem Mateusz a to jest 1. odcinek kursu programowania w technologii WebGL. Jest to kurs przeznaczony dla programistów, więc wymagam od ciebie na starcie umiejętności programowania wmiarę sprawnie w języku Java Script i znajomości technologii webowych, ale nie wymagam od ciebie znajomości rzeczy związanych z grafiką, bo tego właśnie będziemy się tutaj uczyć.

Co będzie w tym kursie: będą przede wszystkim:

  • podstawowe zagadnienia związane z samą grafiką 3D, jak np. co to są siatki, co to są tekstury, jak się tworzy materiały itd.
  • podstawy matematyki i geometrii które są niestety niezbędne takie jak np. co to są wektory, operacje na wektorach
  • implementacja tego wszystkiego w Javascripcie

A więc co to jest WebGL? Jest to API dostępne z poziomu przeglądarki. Jest dostępne w języku Java Script. Wszyscy myślą, że umożliwia ono wyświetlanie grafiki 3D, co nie do końca jest prawdą, bo tak naprawdę daje nam tylko dostęp do GPU (karty graficznej), przy czym aby wyświetlić grafikę 3D musimy sami zadbać o obliczenia geometryczne itd.

Dlatego uprościmy sobie sprawę i skorzystamy z biblioteki. Taką biblioteką jest Three.js. Jest to biblioteka open source, dostępna na GitHubie. Ma 63 tys gwiazdek na GitHubie, więc patrząc na wszystkie biblioteki Javascriptowe, które są na GitHubie, jest na 8. miejscu, zaraz za Node.js, przed Material-UI. Jest to biblioteka dostępna na licencji MIT, więc można ją spokojnie wykorzystywać do celów komercyjnych.

Na swoim GitHubie przygotowałem demko. Można sobie ściągnąć to repozytorium. Jest to standardowy projekt na Webpacku, więc z konsoli wykonujemy najpierw yarn install, nastepnie yarn start, i na http://localhost:8080/ mamy kręcący się sześcian.

[pl] Jak poprawnie wysyłać obrazki w mailach

English version

Czy zdarzyło ci się kiedyś otworzyć maila, w którym tekst wyświetla się poprawnie, ale nie załadowały się obrazki? Wtedy twój program pocztowy wyświetla ci komunikat o tym, że obrazki zostały zablokowane i musisz kliknąć by je odblokować.

To częsty problem. Ba, ciężko znaleźć maile tego problemu pozbawione, w których obrazki ładują się poprawnie (no chyba że wcześniej dodałeś nadawcę do zaufanych). Jest to problem tak powszechny, że wiele ludzi, z którymi na ten temat rozmawiałem już go nie zauważają, albo uważają, że z tym się nie da nic zrobić i to jest normalne.

Ale odpowiedź na pytania czemu tak się dzieje i jak temu zaradzić jest stosunkowo prosta.

Powód

Maile są wysyłane w formie HTML, dlatego też programiści podchodzą do tworzenia szablonów maili jak do tworzenia zwykłych stron www – ale maile stronami www nie są.

Na normalnej stronie internetowej, aby dodać obrazek wstawiasz link do obrazka wewnątrz tabu <img>. Dla przeglądarki nie ma znaczenia czy obrazek jest na tym samym, czy na innym serwerze. Dlatego przy tworzeniu szablonów maili najprostsze wydaje się wrzucić obrazki na jakiś serwer i wstawić pełny url do <img> w mailu.

Tylko, że takie działanie jest świetną okazją dla spamerów. Jeśli ktoś rozsyła spam, chciałby wiedzieć, kto to w ogóle odczytał. Wtedy będzie wiedział, na które maile warto spam wysyłać, a które są np. przez nikogo nieużywane. W takiej sytuacji spamer może dodać do każdego maila <img> z unikalnym url-em, ale kierującym do jego serwera. Kiedy ofiara otworzy maila, aby wyświetlić obrazek program pocztowy musi połączyć się z serwerem spamera. Z tego właśnie powodu programy pocztowe blokują obrazki.

Rozwiązanie

Aby wysłać maila z obrazkiem musisz dodać go jako załącznik. To rozwiązuje problem, gdyż taki obrazek jest przesyłany razem z treścią maila i nie ma potrzeby odpytywania zewnętrznych serwerów.

Dodając załącznik należy nadać mu ContentID (cid), czyli identyfikator, po którym będzie można się odwołać wewnątrz html-a.

<img src="cid:your_content_id" alt="">

Nie zapomnij też zaznaczyć załącznika jako inline, dzięki czemu nie wyświetli się jako standardowy załącznik. Jak dokładnie to zrobić zależy od sposobu w jaki wysyłasz maile.

Alternatywnie mógłbyś użyć obrazka zakodowanego w base64 i url-a typu data:, ale nie jest to wspierane we wszystkich programach pocztowych, szczególnie w Microsoft Outlook. Jak wspomniałem wcześniej, mail nie jest stroną www i możesz wykorzystać w nim tylko ułamek możliwości przeglądarek internetowych, jeśli chcesz mieć pewność, że wszyscy odczytają twoje wiadomości poprawnie.

[pl] Javascript tricks #1 – rozszerzanie prototypów

English version

Javascriptowe tablice mają kilka metod, które pozwalają operować na danych wg. podejścia funkcyjnego (np. filtermapreduce). 

const input=[
    {name: "John", points: 5},
    {name: "Bob", points: 0},
    {name: "Alice", points: 4}];
const result = input.filter(x=>x.points>0).map(x=>x.name);
console.log(result);// => ["John", "Alice"]

Przy czym są to tylko podstawowe metody, nie mamy tak dużych możliwości jak np. biblioteka LINQ w C#.

Istnieją biblioteki do JS o większych możliwościach. Przykładem niech będzie Lodash:

var _ = require('lodash');

const input=[
    {name: "John", points: 5},
    {name: "Bob", points: 0},
    {name: "Alice", points: 4}];
const result = _.map(_.filter(input, x=>x.points>0),x=>x.name); 
console.log(result);// => ["John", "Alice"]

Lodash daje nam dość dużo metod do wykorzystania, jednak tych metod nie wykonujemy na tablicy, tylko tablicę przekazujemy jako parametr metody. Różnica może niewielka, ale gdy chcemy wywołać metody jedna pod drugiej (jak w przykładach powyżej) kod robi się nieczytelny: nie wykonuje się od lewej do prawej, tylko niejako od środka na zewnątrz.

Extensions

W C# jest coś takiego jak „extension method” (pozwolę sobie nie tłumaczyć na siłę na „metody rozszerzające” jak robi to oficjalna dokumentacja tylko zostanę przy oryginalnej nazwie). Jest to sposób na dodanie metody do klasy bez modyfikacji tej klasy.

public static class A{
    public static string MyMethod(this List<string> a){
        return "Hello, world!";
    }
}

//...

var animals = new List<string>{"cat", "dog", "lion"};
Console.WriteLine(animals.MyMethod()); // => "Hello, world!"

W JS można zrobić coś bardzo podobnego (ale nie identycznego, o czym później). Javascript wykorzystuje dziedziczenie oparte na prototypach, więc wszystkie tablice to obiekty dziedziczące z obiektu Array.prototype. Jest to obiekt wbudowany w przeglądarkę, ale możesz do niego przypisywać nowe property i metody jak do każdego zwykłego obiektu, a zmiany będą widoczny we wszystkich obiektach które po nim dziedziczą (nawet tych utworzonych wcześniej).

const animals = ["cat", "dog", "lion"];
Array.prototype.myMethod=function(){return "Hello, world!";}
console.log(animals.myMethod());// => "Hello, world!"

I w tym momencie doświadczeni programiści Javascriptu prawdopodobnie uznają mnie za jakiegoś heretyka. W tym momencie warto wspomnieć czym różni się to podejście od extensionów w C#. Otóż extension w C# znajduje się w jakimś namespace i jest widoczny tylko wtedy, gdy ten namespace w danym pliku załadujemy. Za to edycja prototypów w JS działa globalnie (na wszystkie tablice wewnątrz danego window, niezależnie w jakim pliku jesteśmy). Ma to swoją nazwę: zanieczyszczenie prototypów (ang. prototype pollution).

Dlatego stosuj tą technikę ostrożnie. Jeśli piszesz projekt w kilka osób: uzgodnij z całym zespołem jeśli chcesz namieszać w globalnych prototypach. Odradzam też robienie tego gdy piszesz bibliotekę dla kogoś.

Jakie metody można sobie dopisać?

Zaprezentuję tutaj kilka użytecznych metod które warto dopisać, wraz z implementacją i przykładami użycia. Stworzyłem bibliotekę npm-ową która je zawiera (ale zachęcam cię do pisania tego typu metod samodzielnie)

npm i prototype-extensions
# LUB
yarn add prototype-extensions

sum

Pierwsza z metod po prostu dodaje wszystkie elementy tablicy do siebie, zwracając liczbę.

const array=[10, 2, 5, 0.5];
console.log(array.sum());// => 17.5

Często jednak nie mamy gołych liczb, ale całe obiekty. Dlatego wygodnie jest móc przekazać funkcję, która wyłuska nam z obiektu liczbę, którą chcemy.

const work=[
{worket: 'Alice', workedHours:8, pricePerHour:20},
{worket: 'Bob', workedHours:10, pricePerHour:15},
{worket: 'John', workedHours:2, pricePerHour:30},
];
const totalCost = work.sum(x=>x.workedHours * pricePerHour);
console.log(totalCost);// => 370

implementacja

if (!Array.prototype.sum) {
    Array.prototype.sum = function (fun = x => x) {
        return this.reduce((sum, item) => sum + Number(fun(item)), 0);
    }
}

Pierwszy if jest żeby się upewnić, że nie nadpisujemy jakiejś już istniejącej metody, którą mógł dodać ktoś inny (lub jeśli pojawi się nowa metoda w samej przeglądarce)

W drugiej linii tworzymy nową funkcję, którą przypisujemy do prototypu tablic (Array.prototype). Jako parametr funkcji przekazujemy funkcję (wiem jak to brzmi, ale trochę na tym polega programowanie funkcyjne) która zamieni nam element tablicy na liczbę. Domyślnym parametrem jest funkcja która zwraca to co przyjęła (bo chcemy, żeby w sytuacji, gdy nie podamy żadnego parametru, były brane elementy tablicy tak jak są).

Jeśli ten zapis jest dla ciebie niezrozumiały oto linia nr 2 w wersji bardziej rozpisanej:

Array.prototype.sum = function (fun) {
    if(!fun){
        fun = function(input){
            return input;
        }
    }
//...

sortBy

Sortowanie danych to dosyć podstawowa operacja. Jednak wbudowana w JS metoda sort wymaga podania mody, która porównuje dwie zmienne ze sobą i zwraca, która z nich jest mniejsza. Dużo wygodniejsze byłoby sortowanie po konkretnej wartości (lub kilku wartościach), która jest liczbą.

const people=[
    {name:'Anna', age:18},
    {name:'Bob', age:25},
    {name:'John', age:17},
];
people.sortBy(x=>x.age);
console.log(people);// => [
                    // =>     {name:'John', age:17},
                    // =>     {name:'Anna', age:18},
                    // =>     {name:'Bob', age:25},
                    // => ];

Implementacja

if (!Array.prototype.sortBy) {
    Array.prototype.sortBy = function (...args) {
        let orders = args.map(x => typeof x == 'function' ? x : y => y[x]);
        let compareFunction = (a, b) => {
            for (let order of orders) {
                let valueA = order(a);
                let valueB = order(b);
                if (valueA > valueB)
                    return 1;
                else if (valueA < valueB)
                    return -1;
            }
            return 0;
        };
        return this.sort(compareFunction);
    }
}

Zakładam, że być może programista chciały posortować po kilku wartościach (jeśli pierwsza wartość jest identyczna posortuj po kolejnej), dlatego użyłem zapisu …args (z parametrów funkcji robi nam tablicę) a następnie pętli for..of.

Dodałem też możliwość, aby nie tylko przekazać parametrem funkcję, ale też nazwę property w formie stringa (dlatego użyłem metody map)

min i max

Jak znaleźć największy lub najmniejszy element tablicy? Można tablicę posortować, a potem wziąć z niej pierwszy/ostatni element, ale to podejście jest dosyć słabe z punktu widzenia wydajności. Dużo wydajniej jest mieć metodę która znajduje jeden element.

const array1 = [5,10,2.2,-30];
console.log(array1.min()); // => -30
console.log(array1.max()); // => 10

W programowaniu obiektowym rzadko spotykamy tablice zawierające same liczby. Raczej mamy całe obiekty, które mają w sobie jakieś wartości liczbowe:

const people=[
    {name:'Anna', age:18},
    {name:'Bob', age:25},
    {name:'John', age:17},
];
console.log(people.min(x=>x.age));// => {name:'John', age:17}

Implementacja

if (!Array.prototype.max) {
    Array.prototype.max = function (fun = x => x) {
        let value = null;
        let object = null;
        for (let item of this) {
            const itemValue = fun(item);
            if (typeof itemValue === 'number' && !isNaN(itemValue) && (value == null || itemValue > value)) {
                value = itemValue;
                object = item;
            }
        }
        return object;
    }
}

groupBy

Czasem przydaje się pogrupować obiekty po jakiejś wspólnej wartości:

const people=[
    {name:'Alice', type:'teacher'},
    {name:'Bob', type:'student'},
    {name:'John', type:'student'}
];
const grouped = people.groupBy(x => x.type);
console.log(grouped); // => Map(2){
                      // =>  "teacher" => [{name:'Alice', type:'teacher'}],
                      // =>  "student" => [{name:'Bob', type:'student'}, {name:'John', type:'student'}]
                      // => }

Można spytać, dlaczego użyłem Map zamiast zwykłego Object? Dlatego że w Map kluczem może być wszystko, co daje nam większą elastyczność, gdzie przy zastosowaniu Object kluczem może być tylko String i Symbol.

Implementacja

if (!Array.prototype.groupBy) {
    Array.prototype.groupBy = function (fun = x => x) {
        const ret = new Map();
        for (const value of this) {
            const key = fun(value);
            if (ret.has(key))
                ret.get(key).push(value);
            else
                ret.set(key, [value]);
        }
        return ret;
    }
}

Przyszłość

W specyfikacji ECMAScript Next zaproponowano nowy operator :: (https://github.com/tc39/proposal-bind-operator), który byłby dużo lepszym rozwiązaniem niż rozszerzanie prototypów, ale w tym momencie jest określany jako stage 0, czyli nie jest nawet pewne czy i kiedy zostanie dodany do specyfikacji JavaScriptu, a nawet jeśli, minie dużo czasu zanim przeglądarki go zaimplementują.

Linki

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Inheritance_and_the_prototype_chain