<?xml version="1.0" encoding="utf-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/">
<channel>
	
	<title>RSS 2.0 Článků kategorie "Tvorba WWW"</title>
	<atom:link href="https://mike.treba.cz/rss/kategorie/tvorba-webu/" rel="self" type="application/rss+xml" />
	<link>https://mike.treba.cz/</link>
	<description>RSS 2.0 Článků kategorie "Tvorba WWW"</description>
	<lastBuildDate>Mon, 30 Dec 2024 22:21:10 +1100</lastBuildDate>
	<language>cs</language>
	<generator>Abstract CMS</generator>
	<sy:updatePeriod>daily</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>

		
		<item>
			<title>Webová typografie: odstavce a odsazování</title>
			<link>https://mike.treba.cz/webova-typografie-odstavce-a-odsazovani/</link>
			<pubDate>Mon, 30 Dec 2024 22:21:10 +1100</pubDate> 
			<comments>https://mike.treba.cz/webova-typografie-odstavce-a-odsazovani/#comments</comments>
			<dc:creator></dc:creator>
			<description><![CDATA[<p><span>Rychlovka z&nbsp;oboru #4. V praxi se často setkáváme s odsazováním pomocí prázdných odstavců. Byť je to řešení naprosto nejsnazší, není úplně správné. Možnosti jsou víceméně dvě. V dnešním článku vám popíšu jednu z nich.&nbsp;</span></p>]]></description>   
			<guid isPermaLink="false">https://mike.treba.cz/webova-typografie-odstavce-a-odsazovani/</guid>
			<content:encoded><![CDATA[<p><span>Rychlovka z&nbsp;oboru #4. V praxi se často setkáváme s odsazováním pomocí prázdných odstavců. Byť je to řešení naprosto nejsnazší, není úplně správné. Možnosti jsou víceméně dvě. V dnešním článku vám popíšu jednu z nich.&nbsp;</span></p> <p>Když chystáme web "na slepo" a grafik nám nepřipraví dostatečně obsáhlou textovou stranu, daný problém zpravidla neřešíme. Pokud ovšem vytváříme novou grafiku již existujícího webu, který má dostatek obsahu pro kontrolu, můžeme formátování textů nachystat mnohem lépe.&nbsp;</p>
<p>První věc, kterou musíme rozhodnout, je odsazování pomocí marginů či paddingů. Setkal jsem se s obojím, ale sám budu vždy preferovat spodní padding oproti hornímu a spodnímu marginu. Marginy mají tu výhodu, že se slijí dohromady, nicméně je to pořád neintuitivní chování, které se musíme v hlavě představit kdykoli, když skládáme libovolné elementy za sebe.&nbsp;</p>
<p>Proto je za mě lepší vždy a jenom spodní odsazení. Pro * &gt; :last-child pak stačí resetovat na nulu a máme hotovo.&nbsp;</p>
<p>Jakmile si nastavníme nějaké globální odsazení základních typografických elementů, můžeme se zamyslet nad celkovou strukturou očekávaných článků. Používá klient 2 nadpisy za sebou? Používá klient nadpisy jen jako grafické zdůraznění nebo se jedná o logické celky, které je potřeba oddělit víc? Má klient rád vzdušné stránky, kde není vše namačkáno na sebe?&nbsp;</p>
<p>Pokud je odpověď na většinu z těchto otázek kladná, stačí nám přidat opravdu jen pár definic, aby se výše uvedené věci začaly dít samy. Tady na blogu to dělám úplně stejně.</p>
<h2>Globální definice</h2>
<p>Začneme jednoduchou ukázkou kódu. Z ní je na první pohled jasné, že všechny seznamy a nadpisy, které budou natvrdo v šabloně musíme redefinovat. Ale ono se stačí zamyslet nad poměrem: Omezený počet elementů v šablonách vs. libovolný počet html textu, který nám do šablony dodá systém? Nutnost ručně přidávat všechny definice pokaždé, když přidám novou typovou šablonu? Ne, děkuji. Raději si těch pár prvků mimo obsahovou oblast zresetuji sám.&nbsp;</p>
<pre><code class="css">h1 {padding-bottom:4rem;}

p,
h2,
h3 {padding-bottom:2rem;}

ul, 
ol, 
table {margin-bottom:2rem;}

h4,
h5 {padding-bottom:1rem;}
</code></pre>
<p>Mějme základní definice odsazení. 40 pixelů pro nadpis první úrovně, 20 pixelů pro zbytek. Následující kód vyřeší groupování jednotlivých sekcí tak, abychom v administraci nemuseli řešit vůbec nic navíc. <em>(Komponenty, x různých html bloků pod sebou, oddělovače...)</em>&nbsp;</p>
<h2>Logický blok začíná nadpisem</h2>
<p>Každý nadpis bude odsazen o dalších 20 <span class="small-text">(10, 5)</span> pixelů, pokud před ním není jiný nadpis stejné nebo vyšší úrovně.&nbsp;</p>
<pre><code class="css">.article-detail {

	*:not(h2) &plus; h2 {padding-top:2rem;} 
	*:not(h2):not(h3) &plus; h3 {padding-top:1rem;} 
	*:not(h2):not(h3):not(h4) &plus; h4 {padding-top:0.5rem;} 

}</code></pre>
<p>Je to takhle jednoduché.&nbsp;</p>
<h2>Dvojité odsazení</h2>
<p>Použití padding-bottom namísto dvojitého marginu má ještě další výhodu, kterou je automatické zvedání hodnot u prvků typu &lt;table&gt;. Tabulka musí být vždy odsazena marginem. Pokud chceme pro náš příklad zvednout její odsazení na dvojnásob, nemusíme se bát spojení marginu s předchozím prvkem.&nbsp;</p>
<p>Chceme-li zdůraznit tabulku, která je přesně mezi dvěma odstavci, stačí jednoduše:</p>
<pre><code class="css">p &plus; table {margin-top:2rem;}
p &plus; table &plus; p {padding-top:2rem;}</code></pre>
<p>Spolu s výchozím odsazením, které prvky zdědily z prvního bloku kódu, máme krásných 40 pixelů z obou stran.&nbsp;</p>]]></content:encoded>
			<image>
    				<url>https://mike.treba.cz/img/2023/renewed/design-art.jpg</url>
			</image>
		</item>
		
		
		<item>
			<title>Antispam: zmírnění ochranných kouzel</title>
			<link>https://mike.treba.cz/antispam-zmirneni-ochrannych-kouzel/</link>
			<pubDate>Sun, 25 Jun 2023 19:04:46 +1100</pubDate> 
			<comments>https://mike.treba.cz/antispam-zmirneni-ochrannych-kouzel/#comments</comments>
			<dc:creator></dc:creator>
			<description><![CDATA[<p>Pustil jsem se do revize svého antispam modulu pro komentáře a&nbsp;koukám, že já se s&nbsp;tím tehdy fakt nepáral. Většinu kontrol jsem zachoval, ale&nbsp;například ty prohozené názvy polí mě fakt vytáčely... Bylo potřeba trochu ubrat. Pár tipů, jak hlídat spam ve vlastních formulářích si můžete přečíst v&nbsp;nejnovějším článku.&nbsp;</p>]]></description>   
			<guid isPermaLink="false">https://mike.treba.cz/antispam-zmirneni-ochrannych-kouzel/</guid>
			<content:encoded><![CDATA[<p>Pustil jsem se do revize svého antispam modulu pro komentáře a&nbsp;koukám, že já se s&nbsp;tím tehdy fakt nepáral. Většinu kontrol jsem zachoval, ale&nbsp;například ty prohozené názvy polí mě fakt vytáčely... Bylo potřeba trochu ubrat. Pár tipů, jak hlídat spam ve vlastních formulářích si můžete přečíst v&nbsp;nejnovějším článku.&nbsp;</p> <h3>Kontrolní otázka</h3>
<p>Jako třeba "<em>Napište, kolik je jedna plus jedna"</em>&nbsp;je sice stará, ale stále efektivní metoda. Ona čeština je mnohdy nejúčinnější antispam. Já kontrolní otázku používám stále, ovšem už delší dobu ji za vás vyplní JavaScript. Robot ale JavaScript neumí <em>(tedy stále v to pevně doufám)</em>, takže by se dál neměl dostat. A kdyby náhodou, je tu ještě spousta dalších kontrol.&nbsp;</p>
<h3>Povinně prázdné pole</h3>
<p>Zatím jsme stále u robotů. Kdyby náhodou první kontrola selhala, je tu ještě skryté pole <em>(stylama v externím souboru, nikoli jako hidden input)</em>, které musí zůstat prázdné, jinak se formulář neodešle. Spamboti zpravidla vyplňují vše, co ve formu naleznou. Alespoň dřív tomu tak bylo. A když pole pojmenujeme nějak výstižně, například "skype" nebo "company", robot se na něj chytí. Tato pole jsem měl dvě, ale jedno musí stačit, a tak šlo druhé pryč.&nbsp;</p>
<h3><del>Prohozené názvy polí</del></h3>
<p>Email a web měly prohozený name, a tak když někdo zadal zavináč do pole e-mail <em>(podle labelu určeného pro webovou adresu)</em>, formulář se neodeslal. Funkcionalita chytrá, ovšem působí takové zmatky v procesingu, že od ní budu všechny zrazovat.&nbsp;</p>
<h3><del>Test HTTP_REFERERa</del></h3>
<p>Většina spambotů je chytrá a odesílá stejného referera jako stránky, kam se snaží poslat komentář. Pokud na to autor spambota zapomněl, budu ve výhodě. Užitkem si nejsem příliš jistý, takže vypínám.&nbsp;</p>
<h3>Kontrola odeslaných dat</h3>
<p>Roboty bychom měli vyřešené, takže teď ještě živé spammery. A protože kontrolní otázka je pro pohodlí těch slušných komentujících skrytá, musím nasadit další úroveň kontrol pro živé lidi, co jedou copy&plus;paste&plus;enter na každém webformu, který najdou.&nbsp;</p>
<h4>Spamwords</h4>
<p>Dříve to byla viagra, nyní spammeři nabízejí půjčky. Mám uložený seznam slov, která se nesmí v komentáři objevit. Tohle zpravidla odradí cizokrajné spammery, proti tuzemským to moc efektivní není: proto tu mám co pár týdnů jeden až dva spamy. Což není zlé.</p>
<h4>Počet odkazů v těle zprávy</h4>
<p>Další způsob, jak odradit cizokrajné spammery, je spočítat hypertextové odkazy v těle zprávy. Spamy totiž často obsahují jen kýbl odkazů, a právě proti nim jsem takto chráněný. Spolu s testováním BB kódu v rámci předchozího bodu&nbsp; odchytím velkou část nevyžádaných zpráv. <em>(BB kód nepoužívám, a proto, když se ho někdo snaží odeslat, napíšu mu zprávu, ať to nedělá.)</em>&nbsp;Tady jsem provedl jenom aktualizaci, protože knihovnu jsem psal už hodně dávno, a nepočítala s https variantou odkazů.&nbsp;</p>
<p>Jak vidíte, žádná otravná captcha není potřeba. Těch kontrol je sice požehnaně, ale pohodlí uživatele je na prvním místě.&nbsp;</p>]]></content:encoded>
			<image>
    				<url>https://mike.treba.cz/img/2023/renewed/code-cool.jpg</url>
			</image>
		</item>
		
		
		<item>
			<title>Ajaxové stránkování: včetně funkční aplikace</title>
			<link>https://mike.treba.cz/ajaxove-strankovani-vcetne-funkcni-aplikace/</link>
			<pubDate>Mon, 6 Feb 2023 20:06:35 +1100</pubDate> 
			<comments>https://mike.treba.cz/ajaxove-strankovani-vcetne-funkcni-aplikace/#comments</comments>
			<dc:creator></dc:creator>
			<description><![CDATA[<p>V <a href="https://mike.treba.cz/ajaxove-strankovani-zaklady/">prvním dílu</a> jsem přiblížil, jak nalepit Ajaxové <a href="https://mike.treba.cz/pokrocile-strankovani-php/">stránkování</a> na hotový výpis. S druhým dílem jsem se o dva roky zapomněl. Postup se za tu dobu ovšem nezměnil, tak doufám, že i po takové pauze přinese článek někomu potřebné odpovědi. </p>]]></description>   
			<guid isPermaLink="false">https://mike.treba.cz/ajaxove-strankovani-vcetne-funkcni-aplikace/</guid>
			<content:encoded><![CDATA[<p>V <a href="https://mike.treba.cz/ajaxove-strankovani-zaklady/">prvním dílu</a> jsem přiblížil, jak nalepit Ajaxové <a href="https://mike.treba.cz/pokrocile-strankovani-php/">stránkování</a> na hotový výpis. S druhým dílem jsem se o dva roky zapomněl. Postup se za tu dobu ovšem nezměnil, tak doufám, že i po takové pauze přinese článek někomu potřebné odpovědi. </p> <p>V něm si napíšeme takovou mini-aplikaci, kde tohle všechno spojíme dohromady. Vysvětlování bude méně, jelikož je vše popsáno v předchozích článcích. A na závěr dostaneme funkční řešení.</p>
<p>Miniaplikaci jsem se snažil napsat tak, aby tam bylo co nejméně kódu i souborů. Proto je na hulváta všechno v indexu, proto ani neparsuji jedinou GET proměnnou, byť ve všech tutoriálech o mod_rewrite to doporučuji. Cílem je podchytit 3 stavy <em>(výpis článku, filtr podle kategorie, detail článku)</em> s fallbackem do 404ky, tak, aby kódu bylo co nejméně a já vám necpal nějakou strukturu aplikace, kterou vůbec nechcete. Zařazení článku do kategorie PHP: Základy má proto své opodstatnění; článek je určený začínajícím kodérům. </p>
<h2>Kód #1: SQL</h2>
<p>Tabulky mají nějakou základní strukturu a sloupce, které u kterých se očekává, že je mít budou. Do těla článku je zbytečně vkládat nebudu, můžete si je <a href="https://mike.treba.cz/doc/2020/ajaxarticles2/tables.sql.html" target="_blank">prohlédnout zde</a>.</p>
<h2>Kód #2: PHP</h2>
<p>.htaccess na ukázku není potřeba, takže můžeme přejít rovnou ke skriptům. V configu nastavíme přístupové údaje k databázi a root url, více nastavení zatím nepotřebujeme. Obojí si navíc mohu podmínit pro localhost a ostrý server. </p>
<p>Výchozí stránkování bude po čtyřech, abych nemusel do tabulek vkládat kýble testovacích dat. </p>
<h3>config.php</h3>
<pre><code class="php">$config['paging_limit'] = 4;

$config['root'] = 'http://localhost/ajaxarticles2/';

$config['db_host'] = 'localhost'; 
$config['db_user'] = 'root';
$config['db_pass'] = ''; 
$config['db_name'] = 'evil';</code></pre>
<h3>index.php - kostra</h3>
<p>Jak jsem zmínil výše, cílem miniaplikace je co nejmenší množství kódu i souborů, takže vše naplácáme do indexu a opustíme ho až při načtení šablony. To je základní minimum, které je dobré vždy oddělit. Tady už je čistě na vás, jak si vše poskládáte. Já doporučuji alespoň inicializaci nějaké základní třídy, která se o zbytek postará. Pokud tedy nevyužíváte hotový framework.</p>
<p>To jen pro úplnost, ať nemusím každý blok kódu uvádět a popisovat. Proto se nyní pustíme do samotného načítání obsahu. </p>
<pre><code class="php">&lt;?php

// #0 zapnu zobrazeni chyb, pro jistotu
// #1 includy
// #2 funkce
// #3 pripojeni k databazi
// #4 inicializace promenne $tpl, sem budu predavat sablonova data
// #5 nacteni sdilenych komponent (menu), pouze pres standardni request
// #6 sekvecne podminek na filtraci dle kategorie, vypis a detail clanku
// #7 nacteni templaty

?&gt;</code></pre>
<p>Víceméně každý z bloků by měl mít nějaký vlastní layer v systému, já mám oddělenou pouze funkční část od templatů. </p>
<h3>Sdílené komponenty</h3>
<p>A protože je ukázka opravdu jednoduchá, více věcí, které chci načítat na každé stránce, tu nemám. Do následujícího bloku ale přijde vše, co se přímo netýká výpisu článků: vše, co chceme z Ajaxového requestu vyjmout. V uvedeném příkladu tedy pouze hlavní menu. </p>
<p>Z následující ukázky je také jasné, co se asi dělo ve skriptu předtím - aniž byste se hned museli dívat do kódu.  </p>
<pre><code class="php">if (!isAjax()) {
	$cats_query = "
		SELECT * 
		FROM `article_categories` 
		WHERE `active` = '1' 
		ORDER BY `sort_order` ASC
	";
	
	$cats = $sqli-&gt;query($cats_query);
	$tpl['categories'] = array();
	while ($item = $cats-&gt;fetch_assoc()) {
		$item['href'] = $config['root'] . '?cat=' . $item['seourl'];
		$tpl['categories'][] = $item;
	}
}</code></pre>
<h3>Výpis článků</h3>
<p>Ukázce předchází filtrace dle kategorie, ale na tu se můžete podívat až ve výsledném souboru. Je to pouze funkce navíc, abych mohl ukázat, že Ajaxové stránkování jde nalepit na více modulů zaráz. Proměnná $category_condition bude nabývat jedné z následujících hodnot:</p>
<pre><code class="php">$category_condition = ''; // OR
$category_condition = "AND `category_id` = '{$category['id']}'";</code></pre>
<p><span>A nyní živá ukázka stránkovací knihovny. Odpálíme SQL dotaz na počet záznamů, vytvoříme instanci Pagingu a hurá na SQL dotaz pro výběr objektů. Zde jenom připomenu informaci z minulého článku, že ošetření vstupu si třída ohlídá sama. Nemusíme řešit. </span></p>
<pre><code class="php">$total_query = "
	SELECT COUNT(`id`) 
	FROM `articles` 
	WHERE `active` = '1' {$category_condition}
";

$total_row = $sqli-&gt;query($total_query)-&gt;fetch_row();
$total = $total_row[0]; // kvuli php5
$paging_limit = 4;

$paging = new Paging($total, $paging_limit, 'page'); 
$paging-&gt;set_around(1)
       -&gt;set_drop_vars(array('ajax'))
       -&gt;set_paging();
$paging_start = $paging-&gt;get_start();

$tpl['paging'] = array(
	'desktop' =&gt; $paging-&gt;export_paging(),
	'limit' =&gt; $paging_limit,
);

$articles_query = "
	SELECT * FROM `articles`
	WHERE `active` = '1' {$category_condition}
	ORDER BY `timestamp` DESC
	LIMIT {$paging_start}, {$paging_limit}
";

$articles = $sqli-&gt;query($articles_query);
$tpl['articles'] = array();
while ($item = $articles-&gt;fetch_assoc()) {
	$item['href'] = $config['root'] . '?art=' . $item['seourl'];
	$tpl['articles'][] = $item;
}</code></pre>
<p>A to je z PHP vše.</p>
<p>Co mi tu jednoznačně chybí, je podmínka na výběr článků pouze z aktivní kategorie, nemáme-li zúžený filtr. Pro úplnost uvedu, ale ukázku tím komplikovat nechci. Musel bych totiž ještě řešit výjimku z podmínky, kde články z neaktivní kategorie vybrat chci, jsem-li v jejím detailu. Ale to bychom se příliš zamotali v blogové funkcionalitě, když chci jenom ukázat Ajaxové stránkování. Každopádně pro požadované chování stačí zjoinovat tabulky v obou SQL dotazech. Opět na to jdeme nejjednodušší možnou cestou.  </p>
<pre><code class="sql">SELECT a.* // COUNT(a.`id`)
FROM `articles` a
LEFT JOIN `article_categories` c ON c.`id` = a.`category_id`
WHERE a.`active` = '1' AND c.`active` = '1' </code></pre>
<p>A to je vlastně úplně vše. Frontend zde znovu rozebírat nemusím, tomu se věnoval<strong> <a href="ajaxove-strankovani-zaklady/">předchozí článek</a></strong>. Article detail v aplikaci mám, nicméně to není důležitá funkce k tématu. Tu si můžet prohlédnout v náhledu kompletního zdrojáku. </p>
<h2>Bug fix</h2>
<p>Při testování jsem narazil na jeden zajímavý bug, kterým je ukládání do cache na straně prohlížeče. Uživateli se pak zobrazí pouze tělo stránky bez načteného headeru a footeru. </p>
<p>A právě to je důvod těch pár nenápadných drobností v kódu, kterých si možná na první pohled nevšimnete; Ajax Request posílám na jinou URL, než na které obsah zobrazuji při stisku F5. Ono by to tak i správně mělo být <em>(unikátní URL pro unikátní response)</em>, ale člověk často na podobné věci kašle... Proto parametr ?ajax=1, který bug vyřeší. </p>
<p> </p>
<p><a href="https://mike.treba.cz/doc/2020/ajaxarticles2/index.php" target="_blank">Ukázka: index.php</a></p>
<p class="download-links">Kód: <a href="https://mike.treba.cz/doc/2020/ajaxarticles2/index_source.php" target="_blank">index.php</a><br />Kód: <a href="https://mike.treba.cz/doc/2020/ajaxarticles2/templates/index_source.php" target="_blank">templates/index.php</a><br />Kód: <a href="https://mike.treba.cz/doc/2020/ajaxarticles2/tables.sql.html" target="_blank">tables.sql</a><br />ZIP: <a href="https://mike.treba.cz/doc/2020/ajaxarticles2/ajaxarticles2.zip" target="_blank">archív ke stažení zde</a></p>]]></content:encoded>
			<image>
    				<url>https://mike.treba.cz/img/2023/renewed/code-funny.jpg</url>
			</image>
		</item>
		
		
		<item>
			<title>Stylování formulářů snadno a rychle</title>
			<link>https://mike.treba.cz/stylovani-formularu/</link>
			<pubDate>Tue, 15 Dec 2020 19:09:51 +1100</pubDate> 
			<comments>https://mike.treba.cz/stylovani-formularu/#comments</comments>
			<dc:creator></dc:creator>
			<description><![CDATA[<p>Než si dáme další článek z dílny seriálového kritika, proložím své recenze jedním z oboru. Takový už jsem nepsal ani nepamatuji, ale snad si vzpomenu, jak se to dělá. A protože mé návody byly vždy cílené na kodéry začínající a mírně pokročilé, vezmu si pod lupu stylování formulářů. Úplné základy. </p>]]></description>   
			<guid isPermaLink="false">https://mike.treba.cz/stylovani-formularu/</guid>
			<content:encoded><![CDATA[<p>Než si dáme další článek z dílny seriálového kritika, proložím své recenze jedním z oboru. Takový už jsem nepsal ani nepamatuji, ale snad si vzpomenu, jak se to dělá. A protože mé návody byly vždy cílené na kodéry začínající a mírně pokročilé, vezmu si pod lupu stylování formulářů. Úplné základy. </p> <p>Je to nutné zlo, které člověk musí trpět poměrně často. Ale technologie pokročily, takže se s tím dá poprat velice snadno i bez použití složitých frameworků. Ty buď máte zvládnuté a neřešíte nic, nebo vám činnost spíš zkomplikují. U formulářů to platí dvojnásob.</p>
<h2>Základní resety</h2>
<p>Relativní jednotky zatím řešit nebudeme. Hezky všechno v pixelech, ať v tom nedělám ještě větší zmatky. </p>
<pre><code class="css">* {
	border:0;
	margin:0;
	padding:0;
	-webkit-box-sizing:border-box;
	box-sizing:border-box;
}
:focus {outline:none;}

button,
input,
textarea,
select {
	-webkit-appearance:none;
	-moz-appearance:none;
	appearance:none;
	border-radius:0;
}</code></pre>
<p>Appearance je nejdůležitější vlastnost, které je třeba se zbavit, ať nám to neblbne na telefonech a iOS zařízeních. A pak můžeme úplně v klidu nastavit globální styl pro základní formulářové prvky. Dřív platilo, že hvězdičkový reset na formulářové prvky ne. To už naštěští nějakou dobu nemusíme řešit, takže ušetříme spoustu zbytečností ve stylech.</p>
<p><strong>Nesmíme zapomenout</strong> nastavit kompletní vlastnosti fontu. Tohle bohužel mnohé prohlížeče neumí zdědit z body. Stejně tak <strong>box-sizing</strong>: nemáme-li ho v resetu, je potřeba nastavit pro uvedené elementy. Pro textareu zvedneme výšku a omezíme <strong>resize</strong> na směr i hodnoty.</p>
<pre><code class="css">body, 
input, 
textarea, 
select, 
button {
	font:400 16px/1.5 Arial,Helvetica,sans-serif;
}

input:not([type="radio"]):not([type="checkbox"]),
select,
textarea {
	display:block;
	color:#000;
	padding:10px 15px;
	height:50px;
	width:100%;
	border:1px solid #e1e1e1;
	/*
	-webkit-box-sizing:border-box;
	box-sizing:border-box;
	*/
}

textarea {
	height:200px;
	min-height:100px;
	max-height:1000px;
	resize:vertical;
}</code></pre>
<p>A to je k základním prvkům vše. Všimněte si, že základní pole nemám jako výčet hodnot, ale jako výjimku ze všech. HTML5 nám nabízí mnohem vyšší škálu typů, než jenom [text] a [password]. </p>
<p>Tlačítko si pak můžeme upravit jakkoli uznáme za vhodné, já ho vykreslím jen jako šedý obdélník. Pod tag &lt;form&gt; ho mám vnořené cíleně, protože grafik nám často naplánuje různě vypadají buttony pro formulář a mimo něj. </p>
<pre><code class="css">form button, 
form input[type="submit"] {
	cursor:pointer;
	background:#e1e1e1;
	transition:.25s;
	min-width:100px;
	border:0;
	height:50px;
}
form button:hover, 
form input[type="submit"]:hover {
	background:#888;
	color:white;
}</code></pre>
<h2>Placeholder</h2>
<p>Než nám moderní technologie povolily atribut placeholder, museli jsme výchozí hodnoty složitě měnit JavaScriptem. Nyní je ale vše mnohem jednoduší, snad s výjimkou stylování. Tam malý zádrhel je. Vlastnosti musí být zapsané tak, jak je mám na následující ukázce. Pokud totiž vše zapíšeme za sebe s čárkou, IE Edge nás s tím pošle do háje. Takže <strong>pro každý prefix jedna ukončená definice</strong>. </p>
<pre><code class="css">textarea::placeholder,
input::placeholder {color:#e1e1e1;} 

textarea:-ms-input-placeholder,
input:-ms-input-placeholder {color:#e1e1e1;}

input:not([readonly]):not([disabled]):focus,
textarea:not([readonly]):not([disabled]):focus {border-color:#a1a1a1;}</code></pre>
<h2>Grafický selecbox</h2>
<p>Další problematický prvek je selectbox. V minulosti bylo potřeba na jeho složitější vykreslení povolat JavaScriptovou knihovnu, nyní se však obejdeme bez ní. Pokud ale potřebujeme komplexnější prvky uvnitř drop-down nabídky, čistým stylováním to bohužel nezvládneme. JS knihoven jsem vyzkoušel větší množství, ale nakonec jsem vždy došel k závěru, že je lepší, když si to prostě napíšu sám. Žádná není na 100 % dokonalá. U každé narazíme na nějaké bugy, či události, které neumí. Nejnovější knihovnička mi zabrala asi 3 hodiny, což zhruba odpovídá času, který bych strávil studiem a debugováním nějakého hotového řešení... Často je lepší, když si to člověk napíše sám.</p>
<p>Ale zpět ke stylování klasického selectu. Jediné omezení, které musíme překousnout, je nemožnost upravit samotný drop-down. Jediné, k čemu nás prohlížeče pustí, je velikost písma a barvy. </p>
<p>Hlavní select se dá ovšem nastylovat se vším všudy, bez jakýchkoli problémů. Pokud stále řešíme IE 11, definici backgroundu je nutné rozepsat, protože IE 11 ji nepochopí, když obsahuje funkci calc(). A já raději použiju calc, než abych naschvál ukládal png obrázek s bílým místem, kvůli marginu.</p>
<pre><code class="css">form select {
	padding-right:50px;
	cursor:pointer;
	background:url(select.png) no-repeat;
	background-position:calc(100% - 15px) 50%;
	background-size:20px auto;
}
form select option {
	font-size:110%;
	color:#013be0;
}
form select option:hover {
	background:#013be0;
	color:white;
}
form select::-ms-expand {
	display:none;
}
form select:focus {
	border-color:#a1a1a1;
	color:black;
	text-shadow:0 0 0 #000;
	font-weight:400;
}</code></pre>
<h2>Grafický checkbox a radio</h2>
<p>Ani zde není potřeba volat JavaScript a stylovat prvky pomocí nastrčených spanů. Valná většina moderních prohlížečů umí vykreslit základní styly pro checkbox i radio správně, výjimkou budiž jen Internet Explorer 11. Ale ani on není extra pozadu; "škaredě" vykreslí pouze puntík, respektive fajfku při stavu :checked. </p>
<pre><code class="css">form input[type="radio"],
form input[type="checkbox"] {
	margin:0 15px 0 0;
	font-size:24px;
	width:1em;
	height:1em;
	border:2px solid #e1e1e1;
	border-radius:0;
	display:inline-block;
}
form input[type="checkbox"]:checked {
	border-color:#013be0;
	background:url(checkbox.png) no-repeat 50% 50%/80% auto;
}

form input[type="radio"] {
	border-radius:50%;
	background:url(radio.png) no-repeat -100px 50%/50% auto;
}
form input[type="radio"]:checked {
	border-color:#013be0;
	background-position:50% 50%;
}
form label.radio,
form label.checkbox {
	display:flex;
	align-items:center;
}</code></pre>
<p>Není to vůbec nic složitého: nějaké barvy a obrázky, zarovnání a odsazení, a máme hotovo. Pokud chceme stejně hezké prvky i pro zmiňovaný IE 11, je potřeba přidat pomocné spany, ale i tady se úspěšně vyhneme JS obsluze. Stačí přidat pár CSS definic pro posloupné prvky. To už si ale prohlédněte na přiložené ukázce. </p>
<h2>Závěrem: přístupnost na prvním místě</h2>
<p>Na závěr bych ještě rád zmínil jednu věc, kterou vídám v poslední době stále častěji. Mám na mysli různé "cool" efekty, které mají formuláře takříkajíc zkrášlit. Máme label, který je vepsaný přímo přes textové pole a supluje funkci placeholderu. Na focus popisek vyjede nahoru přes definovanou transition. Určitě už jste to někdy viděli. Funkce je to sice pěkná, ale zoufale nepraktická... Problém nastává ve chvíli, kdy nám prohlížeč automaticky jednotlivá pole vyplní. JavaScript totiž neumí univerzálně detekovat tuto událost, a tak se nám může stát, že label je překrytý přes hodnotu v poli. To sice lze vyřešit několika způsoby, ale my bychom si spíš měli položit otázku: proč bych měl tohle vůbec ladit?</p>
<p>Jako uživatel chci formulář přehledný a jednoduchý, jako kodér chci formulář... můžete hádat, jaký. Není formulář v příloze sympatičtější, než nějaká pekelná monstra, co vám třikrát obletí svět, než vás pustí k vyplnění? </p>
<p class="download-links">Ukázka: <a href="doc/2020/form/" target="_blank">form.html</a></p>]]></content:encoded>
			<image>
    				<url>https://mike.treba.cz/img/2020/web/pexels-photo-code-2.jpeg</url>
			</image>
		</item>
		
		
		<item>
			<title>Ajaxové stránkování: základy</title>
			<link>https://mike.treba.cz/ajaxove-strankovani-zaklady/</link>
			<pubDate>Wed, 29 Apr 2020 21:59:33 +1100</pubDate> 
			<comments>https://mike.treba.cz/ajaxove-strankovani-zaklady/#comments</comments>
			<dc:creator></dc:creator>
			<description><![CDATA[<p>Úpravy vlastního webu mi daly podklady pro nový článek, který se bude věnovat Ajaxovému stránkování. Tedy přesněji, jak nalepit Ajax na existující výstup. Budu předpokládat, že nějaký výpis článků z databáze už máte hotový a podívám se detailněji na úpravy, kterými musí systém projít. Složité úpravy to určitě nebudou. Článek bude směrovaný k začínajícím kodérům. </p>]]></description>   
			<guid isPermaLink="false">https://mike.treba.cz/ajaxove-strankovani-zaklady/</guid>
			<content:encoded><![CDATA[<p>Úpravy vlastního webu mi daly podklady pro nový článek, který se bude věnovat Ajaxovému stránkování. Tedy přesněji, jak nalepit Ajax na existující výstup. Budu předpokládat, že nějaký výpis článků z databáze už máte hotový a podívám se detailněji na úpravy, kterými musí systém projít. Složité úpravy to určitě nebudou. Článek bude směrovaný k začínajícím kodérům. </p> <p>Pošlu request, dostanu výstup, vepíšu výstup: nejkratší možná cesta, nic víc. Až v závěru článku nastíním, jak by se daná funkcionalita mohla <em>(měla)</em> řešit lépe. Ale věřím, že pro začátek to takhle stačí. </p>
<h2>Co musí být hotové</h2>
<p>Abych nemusel rozebírat úplné a naprosté základy, naopak zase předpokládám, že to hlavní už je hotové. Musíme mít výpis článků z databáze. K tomu stránkování. Jak bude vypadat a jak se bude chovat, na tom nezáleží: musí prostě jen fungovat. Také bychom měli mít aspoň minimálně oddělený kontroler od šablony, abychom mohli využít podmíněné tahání jednotlivých bloků stránky. </p>
<h2>Co musíme upravit</h2>
<p>V prvé řadě <strong>šablonu</strong> tak, abychom měli jasně ohraničený cíl, kam se bude vpisovat obsah. Dále <strong>kontrolery</strong>. Pokud posílám Ajax request na výpis článků, nechci přece zbytečně načítat celé menu nebo novinky někde v pravém sloupci. <strong>Přidáme funkci</strong> na detekci Ajax requestu, abychom to nemuseli podmiňovat GET proměnnou. <em>(Kterou sice stále využijeme, ale k jinému účelu.)</em> Dále loadovací kolečko: můžeme přidat, nemusíme. Tedy úprava <strong>stylů</strong>. No a na závěr samozřejmě celá <strong>JavaScriptová obsluha</strong>. </p>
<p>Úprava kontrolerů je přímo úměrná složitosti systému, ale tož nemusíme to osekat úplně všechno. Někdy je lepší provést pár operací navíc, než abych podmiňoval každou akci v každém layeru systému, aby pak vznikl nějaký <br /><a href="https://mike.treba.cz/img/2017/www/nth/wtf_per_minute.png" rel="lightbox" target="_blank">takovýto kód</a>...</p>
<h3>Detekce Ajax requestu</h3>
<pre><code class="php">function isAjax() {
	return isset($_SERVER['HTTP_X_REQUESTED_WITH']) &amp;&amp; (strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) === 'xmlhttprequest');
}</code></pre>
<h3>Příklad načtení obsahu</h3>
<p>Jak vyřešit architekturu systému a routování Ajaxových a běžných requestů, o tom dnešní článek také není. Buď vyřešeno máte, nebo se s tím nebudete trápit. Právě takové řešení uvedu v následujícím příkladu. Jednoduchou podmínkou oddělíme volání funkcí respektive metod, které mají být vykonané. Třeba nějak takhle. Pokud bychom tuto úpravu neprovedli, samotný Ajax pak ztratí tak trochu smysl. </p>
<pre><code class="php">function loadStuff(){
	
	$ajax = Server::isAjax();
	
	loadArticles();
	loadTagsForArticles();
	
	createPaging();
	getPageTitle();
	
	if(empty($ajax)){
		loadCategoryMenu();
		loadArchiveMenu();
		loadTagCloud();
		
		// ...
	}

	include_once('template.phtml');

}</code></pre>
<h2>Šablona</h2>
<p>V šabloně potřebujeme vyřešit reálné zobrazení. Určitě je jednodušší dostat celý kód a někam ho fláknout, než parsovat bůhvíjaké pole a někam ho pochybně generovat. Pořád se bavíme o webu náročnosti toho mého, nikoli o komplexní JavaScriptové aplikaci. Takže z šablony vyjmeme části, které se měnit nebudou. Včetně containeru, kam se vloží výsledek requestu. </p>
<pre><code class="php">&lt;?php
	$ajax = isAjax();
?&gt;
&lt;?php if(empty($ajax)){ ?&gt;
	
	&lt;?php include_once('_header.phtml'); ?&gt;
	
	&lt;div class="article-list"&gt;
		&lt;div class="js-ajax-main-container"&gt;

&lt;?php } ?&gt;
		
		&lt;?php // foreach co vypise clanky ?&gt;
		
		&lt;?php // strankovani ?&gt;
	
&lt;?php if(empty($ajax)){ ?&gt;	
		
		&lt;/div&gt;
		&lt;div class="ajax-loader js-ajax-loader hidden"&gt;&lt;div class="circle"&gt;&lt;/div&gt;&lt;/div&gt;
	&lt;/div&gt;

	&lt;?php include_once('_footer.phtml'); ?&gt;
	
&lt;?php } ?&gt;</code></pre>
<h3>Styly</h3>
<p>Než se podíváme na nejdůležitější část kódu, JavaScript, sfoukneme ještě styly. Přidáme si loadovací kolečko tak, aby bylo vždy vycentrované ve viditelné oblasti a zároveň překrývalo celý container. Samozřejmě je potřeba, aby náš "<em>js-ajax-main-container</em>" byl obalen ještě jediným divem s relativní pozicí, aby se absolutní chytila správně. Pozici kolečka pak zařídí výška relativně k viewportu a <strong>position: sticky.</strong>  <em>(Fixní alternativa je fallback pro IE11.)</em> </p>
<pre><code class="css">.hidden {display:none !important;}

.article-list {position:relative;}

.ajax-loader {
	position:absolute;
	top:0;
	left:0;
	width:100%;
	height:100%;
	z-index:20000;
}

.ajax-loader .circle {
	position:fixed;
	position:sticky;
	top:0;
	left:0;
	width:100%;
	height:100%;
	max-height:100vh;
	background:rgba(255,255,255,0.75) url(loading.gif) no-repeat 50% 50%/150px auto;
}</code></pre>
<h2>JavaScriptová obsluha</h2>
<p>A to nejdůležitější na závěr. Kontroler máme upravený, načítá jen nutná data. Šablona zobrazí přesně to, co potřebujeme. Takže teď se jenom napojíme na stránkování a namísto přechodu na cílovou URL provedeme request. Ještě připomenu, že stránkování je také potřeba přepsat po requestu, protože pokud ho například vypisujeme nějak takto: </p>
<pre style="text-align: center;"> 1 ... 7 | <strong>8</strong> | 9 ... 12</pre>
<p>samotné přehození<strong> active class</strong> nestačí. A opět je jednodušší vepsat celý HTML kód, než tuhle hrůzu generovat přes JS. Když už ji umíme vypsat přes PHP. Což také znamená, že po requestu musíme znovu namapovat akce na odkazy. </p>
<pre><code class="js">var hookActions = function(){
	$('.paging').find('a').on('click touch', function(){
		var url = $(this).attr('href');
		contentRequest(url, false);
		return false;
	});
	
	// plus vse ostatni, co se nejak napojuje na elementy ve vypisu clanku
};

hookActions();</code></pre>
<p>Samotná funkce <strong>contentRequest </strong>je mega easy. Pošle request, dostane html výstup, přepíše innerHTML a konec. Ale abychom si to udělali víc cool a sexy, provedeme i změnu URL bez jejího znovunačtení. K tomu slouží metoda <strong class="color-blue">history.pushState</strong>. Takže pokud kliknu např. na stránku 2, stisknu F5, už na ní zůstanu. A nemusím to ohýbat přes kotvy a podmíněné volání funkce. Server prostě vrátí normální output. </p>
<pre><code class="js">var loader = $('.js-ajax-loader');
var target = $('.js-ajax-main-container');
var currentUrl = window.location.href.split('#')[0];

var contentRequest = function(url, historyMove){
	loader.removeClass('hidden');
	
	var requestUrl = url &plus; (url.indexOf('?')&plus;1 ? '&amp;' : '?') &plus; 'ajax=1';
	
	$.ajax({
		url: requestUrl
		success: function(data){
			target.html(data);
			
			if(!historyMove){
				$('html,body').stop(true,true).animate({ scrollTop: 0 }, 0);
				history.pushState(null, null, url);
			}
			
			currentUrl = url;
			
			hookActions();
			loader.addClass('hidden');
		},
		error: function(){
			window.location.href = url;
		}
	});
};</code></pre>
<p>Jenže když už jednou šáhnu na <strong>history.pushState</strong>, měl bych vyřešit i nativní funkce prohlížeče <strong>zpět</strong> a <strong>vpřed</strong>. Pokud uživatel přijde například z Googlu na druhou, třetí stránku výpisu, párkrát si dole klikne na paging, tlačítko zpět by ho vrátilo zase na Google. Protože reálně se po stránce nepohyboval. Ale to lze vyřešit přidáním jediné události: </p>
<pre><code class="js">$(window).bind('popstate', function(e){
	var nextUrl = window.location.href.split('#')[0];
	
	if(nextUrl != currentUrl){
		contentRequest(window.location.href, true, true);	
	}
	
	currentUrl = nextUrl;
});</code></pre>
<p>Tady jsem měl trochu problém s odpálením popstate při změně <strong>window.location.hash</strong>. Přes hash totiž zaměřuji jednotlivé komentáře, a on se na to <strong>popstate</strong> také chytá. Proto parsování url a jedna malá podmínka: otestuji, jestli se změnila celá adresa nebo jenom kotva. </p>
<p>V tuto chvíli je vám také určitě jasné, k čemu slouží druhý parametr u funkce <strong>contentRequest</strong>: jakmile se hýbu v historii, nebudu znovu vyvolávat její změnu, nebo bych se z toho zacyklil. </p>
<p>A co se vlastně stane při vypnutém JavaScriptu? No přece to samé, co před nasazením Ajaxu. Stránka se načte přes normální request. Na závěr ještě přiložím link na jednotlivé části kódu v samostatných souborech, pokud byste měli zájem si je uložit. </p>
<h2>Lepší řešení</h2>
<p>JSON, samozřejmě. Pořád budu vracet celý HTML kód, ale díky poli dostanu možnost měnit třeba titulek. Nebo si do patičky zapsat čas trvání reqeustu. Pro účely vývoje. Ale to už bych nemohl článek oštítkovat jako "Základy", navíc by šlo o příliš individuální úpravy, které těžko zobecním jedním tutoriálem.  </p>
<p>Také bychom mohli přidat rozdílné chování pro telefon: stránkování se nahradí jenom za odkaz "<em>Následující</em>" a funkce obsah přidá, nepřepíše. Ale to už by pro změnu bylo na nový článek. A protože druhý díl už mám nachystaný a chci v něm ukázat funkční miniaplikaci, o mobilním stránkování třeba zase jindy.  </p>
<p class="download-links"><a href="https://mike.treba.cz/doc/2020/ajaxarticles/index.php?f=func" target="_blank">func.php</a><br /><a href="https://mike.treba.cz/doc/2020/ajaxarticles/index.php?f=load" target="_blank">loadstuff.php</a><br /><a href="https://mike.treba.cz/doc/2020/ajaxarticles/index.php?f=temp" target="_blank">template.php</a><br /><a href="https://mike.treba.cz/doc/2020/ajaxarticles/style.css" target="_blank">style.css</a><br /><a href="https://mike.treba.cz/doc/2020/ajaxarticles/main.js" target="_blank">main.js</a></p>
<p class="download-links"><a href="https://mike.treba.cz/doc/2020/ajaxarticles/ajaxarticles.zip" target="_blank">ZIP archív ke stažení</a></p>]]></content:encoded>
			<image>
    				<url>https://mike.treba.cz/img/2023/renewed/keybord.jpg</url>
			</image>
		</item>
		
		
		<item>
			<title>CSS pseudo-třídy: nth-child, nth-of-type, nth-last-of-type</title>
			<link>https://mike.treba.cz/css-pseudo-tridy-nth-child-nth-of-type-nth-last-of-type/</link>
			<pubDate>Thu, 23 Mar 2017 20:05:27 +1100</pubDate> 
			<comments>https://mike.treba.cz/css-pseudo-tridy-nth-child-nth-of-type-nth-last-of-type/#comments</comments>
			<dc:creator></dc:creator>
			<description><![CDATA[<p>Článek z oboru slibuji už dlouho. A čím lepším pokračovat, než pořádným seznamem tipů a triků, jak snáz stylovat web. Dnes se podíváme některé pseudo-třídy kaskádových stylů, které vám usnadní stylování dlouhých seznamů. Ale i krátkých. Také se pokusím vysvětlit, proč tyto selektory nefungují vždy tak, jak bychom si představovali.</p>]]></description>   
			<guid isPermaLink="false">https://mike.treba.cz/css-pseudo-tridy-nth-child-nth-of-type-nth-last-of-type/</guid>
			<content:encoded><![CDATA[<p>Článek z oboru slibuji už dlouho. A čím lepším pokračovat, než pořádným seznamem tipů a triků, jak snáz stylovat web. Dnes se podíváme některé pseudo-třídy kaskádových stylů, které vám usnadní stylování dlouhých seznamů. Ale i krátkých. Také se pokusím vysvětlit, proč tyto selektory nefungují vždy tak, jak bychom si představovali.</p> <h2>HTML kód</h2>
<p>Mějme následující HTML kód. Ten může reprezentovat například výpis produktů na e-shopu, výpis novinek nebo čehokoli jiného. Container obsahuje prvky .item zakončené vyčištěním floatů.</p>
<pre><code class="html">&lt;div class="itemlist"&gt;
	&lt;div class="item"&gt;&lt;/div&gt;&lt;div class="item"&gt;&lt;/div&gt;&lt;div class="item"&gt;&lt;/div&gt;
	&lt;div class="item"&gt;&lt;/div&gt;&lt;div class="item"&gt;&lt;/div&gt;&lt;div class="item"&gt;&lt;/div&gt;
	&lt;div class="item"&gt;&lt;/div&gt;&lt;div class="item"&gt;&lt;/div&gt;&lt;div class="item"&gt;&lt;/div&gt;
	&lt;div class="cleaner"&gt;&lt;/div&gt;
&lt;/div&gt;</code></pre>
<h2>Nth-child: základ</h2>
<p>Pro ty z nás, kteří si v životě nezapamatují, jestli <em>odd </em>nebo <em>even </em>je <em>lichá </em>nebo <em>sudá</em>, existuje velice jednoduchý vzorec. Každý druhý prvek je samozřejmě sudý, při odečtení jedničky od sudého čísla vyjde číslo liché.</p>
<pre><code class="css">.itemlist .item:nth-child(2n) {background:red;}  
.itemlist .item:nth-child(2n-1) {background:blue;}</code></pre>
<p><img src="http://www.mike.treba.cz/img/2017/www/nth/nth-2n.jpg" alt="" /></p>
<h3>Rozsah: od začátku po N-tý</h3>
<p>Často potřebujeme pár prvních elementů odlišit od těch ostatních. Takto zvýrazníme první 3.</p>
<pre><code class="css">.itemlist .item:nth-child(-n&plus;3) {background:blue;}</code></pre>
<p><img src="img/2017/www/nth/nth-range-2.jpg" alt="" /></p>
<h3>Rozsah: od N-tého dál</h3>
<p>Pokud potřebujeme opak výše uvedeného, učiníme tak následovně.</p>
<pre><code class="css">.itemlist .item:nth-child(n&plus;4) {background:blue;}</code></pre>
<p><img src="img/2017/www/nth/nth-range-1.jpg" alt="" /></p>
<h3>Rozsah: průnik definic</h3>
<pre>.itemlist .item:nth-child(-n&plus;6):nth-child(n&plus;4) {background:blue;}</pre>
<p><img src="img/2017/www/nth/nth-range-double.jpg" alt="" /></p>
<h2>Nth-child: kdy to nefunguje</h2>
<p>Při použití n-tého potomka je důležité myslet na jednu věc: pseudo-selektor se <strong>nevztahuje </strong>k CSS třídě, u které je napsán. Takže pokud někam mezi divy vložíme například nějaké &lt;br&gt;, celý princip pořadí prvků se rozbije.</p>
<p><strong>Následující zápisy jsou ekvivalentní:</strong></p>
<pre><code class="css"><strong>.itemlist .item:nth-child(2n) {background:blue;}  
.itemlist :nth-child(2n) {background:blue;}</strong></code></pre>
<p>Proč se tedy mnohem častěji setkáme s podobou zápisu na prvním řádku než na druhém? Odpověď je jednoduchá: přece aby bylo jasné, co tím autor myslel. K čemu přesně je definice myšlena. Druhý řádek proto považuji za monstrózní prasárnu.</p>
<h2>Nth-of-type</h2>
<p>Nth-of-type částečně supluje chování, kterého většinou chceme u podobných výpisů dosáhnout. Na rozdíl od nth-child, která referuje na N-tý prvek DOMu nezávisle na tom, o jaký prvek se jedná, nth-of-type se vztahuje na <strong>tagName</strong>. Takže když si rozšíříme první příklad o nějaké další prázdné prvky, fungovat to bude.</p>
<pre><code class="html">&lt;div class="itemlist"&gt;
	&lt;div class="item"&gt;&lt;/div&gt;&lt;div class="item"&gt;&lt;/div&gt;&lt;div class="item"&gt;&lt;/div&gt;&lt;span&gt;&lt;/span&gt;
	&lt;div class="item"&gt;&lt;/div&gt;&lt;div class="item"&gt;&lt;/div&gt;&lt;div class="item"&gt;&lt;/div&gt;&lt;span&gt;&lt;/span&gt;
	&lt;div class="item"&gt;&lt;/div&gt;&lt;div class="item"&gt;&lt;/div&gt;&lt;div class="item"&gt;&lt;/div&gt;&lt;span&gt;&lt;/span&gt;
	&lt;span class="cleaner"&gt;&lt;/span&gt;
&lt;/div&gt;</code></pre>
<p>Pokud selektor napojíme na .<strong>item</strong>, zacílí právě divy. Ostatní tagy stejného rodiče ignoruje. Samozřejmě v takovém případě nechceme čistící prvek jako div, nýbrž jako span.</p>
<pre><code class="css">.itemlist .item:nth-of-type(2n) {background:blue;}</code></pre>
<p><img src="https://mike.treba.cz/img/2017/www/nth/nth-of-type.jpg" alt="Nth Of Type" /></p>
<h2>Nth-last-of-type</h2>
<p>Má stejný princip jako u předchozího selektoru, akorát prvky bereme odzadu. Tady je opravdu nutné dbát na jména tagů, protože zobrazení se pak může různě přeskakovat či neaplikovat vůbec. Jako příklad uvedu HTML kód se spoustou parazitních tagů, kterým zobrazení definovat nechceme.</p>
<pre><code class="html">&lt;div class="itemlist itemlist_6"&gt;
	&lt;div class="item"&gt;&lt;/div&gt;&lt;div class="item"&gt;&lt;/div&gt;&lt;div class="item"&gt;&lt;/div&gt;&lt;br&gt;
	&lt;div class="item"&gt;&lt;/div&gt;&lt;div class="item"&gt;&lt;/div&gt;&lt;div class="item"&gt;&lt;/div&gt;&lt;br&gt;
	&lt;div class="item"&gt;&lt;/div&gt;&lt;div class="item"&gt;&lt;/div&gt;&lt;div class="item"&gt;&lt;/div&gt;&lt;br&gt;	
	&lt;span class="cleaner"&gt;&lt;/span&gt;
&lt;/div&gt;</code></pre>
<pre><code class="css">.itemlist .item:nth-of-type(3n&plus;1) {background:blue;}
.itemlist .item:nth-last-of-type(1) {background:red;}
.itemlist .item:nth-last-of-type(2n) {background:green;}</code></pre>
<p>První, čtvrtý a sedmý prvek bude modrý. Poslední bude červený. A každý sudý odzadu zase zelený, což samozřejmě přebije modré pozadí prvku čtvrtého :-)</p>
<p><img src="https://mike.treba.cz/img/2017/www/nth/nth-multi.jpg" alt="Nth Multi" /></p>
<h2>N-tý prvek v závislosti na rozlišení</h2>
<p>Na závěr si dáme trochu responzivity. Produktové výpisy s různým počtem prvků podle rozlišení dnes najdeme na každém druhém webu. Možností nastylování je spousta a já bych na závěr představil jednu z nich, samozřejmě za využití nth-of-type. Abychom nemuseli vše 10x redefinovat, omezíme definice rozlišením.</p>
<p>Nejdřív budou na stránce 4 prvky vedle sebe, pak 3 a na závěr 2. A jako malý bonus obarvíme každý druhý řádek jinou barvou. (<em>Tedy skupinu záznamů na daném řádku.</em>) HTML kód bude opět stejný, jako v první ukázce, akorát tam toho bude trochu víc.</p>
<pre><code class="html">&lt;div class="itemlist"&gt;
	&lt;div class="item"&gt;&lt;/div&gt;&lt;div class="item"&gt;&lt;/div&gt;&lt;div class="item"&gt;&lt;/div&gt;
	&lt;div class="item"&gt;&lt;/div&gt;&lt;div class="item"&gt;&lt;/div&gt;&lt;div class="item"&gt;&lt;/div&gt;
	&lt;div class="item"&gt;&lt;/div&gt;&lt;div class="item"&gt;&lt;/div&gt;&lt;div class="item"&gt;&lt;/div&gt;
	&lt;span class="cleaner"&gt;&lt;/span&gt;
&lt;/div&gt;</code></pre>
<p>Každý 4. element bude mít zresetovaný pravý margin, každý 4&plus;1. zresetuje float. Každý 5., 6., 7. a 8. prvek bude zvýrazněný. Skrz omezení na plus a mínus bohužel komplexnější funkci nenapíšeme, a tak je potřeba to takhle vylistovat za sebou.</p>
<pre><code class="css">.itemlist {max-width:900px;margin:0 auto;}
.itemlist .cleaner {clear:both;}
.itemlist .item {width:24.25%;margin:0 1% 10px 0;float:left;height:50px;background:grey;}

@media all and (min-width: 1201px){
	.itemlist .item:nth-of-type(4n) {margin-right:0%;}
	.itemlist .item:nth-of-type(4n&plus;1) {clear:both;}
	
	.itemlist .item:nth-of-type(8n),
	.itemlist .item:nth-of-type(8n-1),
	.itemlist .item:nth-of-type(8n-2),
	.itemlist .item:nth-of-type(8n-3) {
		background:blue;
	}
}

@media all and (min-width: 601px) and (max-width: 1200px){
	.itemlist .item {width:32.666%;}
	.itemlist .item:nth-of-type(3n) {margin-right:0%;}
	.itemlist .item:nth-of-type(3n&plus;1) {clear:both;}
	
	.itemlist .item:nth-of-type(6n),
	.itemlist .item:nth-of-type(6n-1),
	.itemlist .item:nth-of-type(6n-2) {
		background:blue;
	}
}

@media all and (max-width: 600px){
	.itemlist .item {width:49.5%;}
	.itemlist .item:nth-of-type(2n) {margin-right:0%;}
	.itemlist .item:nth-of-type(2n&plus;1) {clear:both;}
	
	.itemlist .item:nth-of-type(4n),
	.itemlist .item:nth-of-type(4n-1) {
		background:blue;
	}
}</code></pre>
<p><img src="https://mike.treba.cz/img/2017/www/nth/nth_list.jpg" alt="Nth List" /></p>
<p>Pokud jsme nuceni jednotlivé prvky cílit nějakým šíleným kaskádovým zápisem, lze stejnou definici zapsat i negací bez nutnosti opakovat 4x pod sebou ".itemlist .item". Ale to už trochu zavání výstižným "WTF" (<em>kde počet WTF za minutu je neoficiální ukazatel kvality kódu</em>).</p>
<p><em>Odkazy na ukázky najdete na konci článku.</em></p>
<pre><code class="css">html.project-237 body.default-article.has-productlist <br /> .container.dark-bg .central-area .itemlist <br /> .item:not(:nth-of-type(8n&plus;1)):not(:nth-of-type(8n&plus;2)):not(:nth-of-type(8n&plus;3)):not(:nth-of-type(8n&plus;4)){
	background:blue;
}</code></pre>
<p><img style="display: block; margin-left: auto; margin-right: auto;" src="https://mike.treba.cz/img/2017/www/nth/wtf_per_minute.png" alt="Wtf Per Minute" /></p>
<h2>Ukázky uvedených řešení</h2>
<p>Vše, co jsme v dnešním článku vyzkoušeli, si můžete prohlédnout na následujících odkazech. Druhý příklad pak reaguje na resize okna prohlížeče.</p>
<p class="download-links"><a href="doc/2017/nth/nth.html" target="_blank">:nth-child, :nth-of-type 1. - 6. příklad</a><br /><a href="doc/2017/nth/nth_2.html" target="_blank">:nth-of-type responzivní ukázka</a></p>]]></content:encoded>
			<image>
    				<url>https://mike.treba.cz/img/2017/www/pen-writing-css-code-feature_1290x688_ms.jpg</url>
			</image>
		</item>
		
		
		<item>
			<title>Velké srovnání heatmap</title>
			<link>https://mike.treba.cz/srovnani-sluzeb-poskytujicich-heatmapy-zdarma/</link>
			<pubDate>Sun, 4 Sep 2016 22:46:03 +1100</pubDate> 
			<comments>https://mike.treba.cz/srovnani-sluzeb-poskytujicich-heatmapy-zdarma/#comments</comments>
			<dc:creator></dc:creator>
			<description><![CDATA[<p>Heatmapy pro mě byly až do nedávna velkou neznámou. Stačilo ale pár malých podnětů a já naznal, že bych měl tuto mezeru ve vzdělání doplnit. Heatmapy totiž přímo nesouvisí s mým pracovním zaměřením; je to analytický nástroj, který pomocí barevných polí ukazuje intenzitu uživatelské interakce. Řečeno jednoduše: vidíte, kam lidé klikají. </p>]]></description>   
			<guid isPermaLink="false">https://mike.treba.cz/srovnani-sluzeb-poskytujicich-heatmapy-zdarma/</guid>
			<content:encoded><![CDATA[<p>Heatmapy pro mě byly až do nedávna velkou neznámou. Stačilo ale pár malých podnětů a já naznal, že bych měl tuto mezeru ve vzdělání doplnit. Heatmapy totiž přímo nesouvisí s mým pracovním zaměřením; je to analytický nástroj, který pomocí barevných polí ukazuje intenzitu uživatelské interakce. Řečeno jednoduše: vidíte, kam lidé klikají. </p> <p>Statistiky pak slouží k optimalizaci aktivních prvků; můžete ladit jejich pořadí, velikost či umístění na stránce. Zjistíte, jak se lidé na vašem webu chovají a můžete přesunout důležité prvky tak, aby byly v "<em>červených oblastech</em>". Tak jsem začal hledat, co bych mohl vyzkoušet. Po chvíli hledání jsem došel na <strong>4 různé služby,</strong> které poskytující heatmapy <strong>zdarma</strong>. Všechny jsem postupně otestoval a jejich srovnání vám nabídnu v dnešním článku.</p>
<h2>1. Heatmap.me</h2>
<p><a class="potlaceno" href="img/2016/www/heatmaps/heatmap.me_sample.jpg" rel="lightbox"><img class="img-bordered" src="http://mike.treba.cz/img/2016/www/heatmaps/heatmap.me_preview.jpg" alt="heatmap.me_preview.jpg" /></a></p>
<p>Hned první služba, kterou jsem zkusil, mě příjemně potěšila. Jedná se o nejjednodušší z testovaných nástrojů, přesto plní svou funkci na výbornou. Instalace je vcelku jednoduchá. Vytvoříte si účet, dostanete vygenerovaný kód (<em>oni ho doporučují vložit do hlavičky, ale funguje i konci kódu</em>) a služba se zapne.</p>
<p>V pravé části webu se zobrazí kontrolní panel, na kterém si nastavíte konkrétní stránky, které chcete monitorovat. Můžete jich mít <strong>aktivních nejvýš 5</strong>. To je asi hlavní nevýhoda... Nešikovné je i samotné zapínání kontrolního panelu: musíte si <strong>pamatovat </strong>neobvyklou klávesovou zkratku <strong>levý alt &plus; shift &plus; H</strong>.</p>
<p>Naopak výhodou je, že se nemusíte přihlašovat nikam do žádné externí stránky a heatmapu vidíte <strong>přímo vepsanou do webu</strong>. Služba běží velice svižně a celkově jsem s ní byl spokojen. Měl jsem trochu pocit, že to měří nějak méně, než by mělo, každopádně to byl asi jen subjektivní dojem. Zpomalení stránek na telefonu jsem nepozoroval.</p>
<h2>2. Mouseflow.com</h2>
<p><a class="potlaceno" href="img/2016/www/heatmaps/mouseflow.com_sample.jpg" rel="lightbox"><img class="img-bordered" src="http://mike.treba.cz/img/2016/www/heatmaps/mouseflow.com_preview.jpg" alt="mouseflow.com_preview.jpg" /></a></p>
<p>Druhá testovaná služba se pak ukázala jako nejkomplexnější a velice nadupaná. Registrace a aktivace proběhla opět bez problémů, snad jen na přesné umístění měřicího kódu je potřeba dbát: musí být před ukončujícím tagem &lt;/body&gt; a nikde jinde.</p>
<p>U Mouseflow jsem byl přímo unesen nahráváním videa: u každého návštěvníka se uloží video jeho přesného pohybu po stránce. Je tam krásně vidět i koncové zařízení; jakým způsobem se mu web zobrazil a vlastně vše, co na stránce prováděl.</p>
<p>Nevýhoda Mouseflow je pak limit 100 nahraných sezení za měsíc u free verze. Což je většinou za den hotovo... Každopádně i 100 návštěv už je slušný statistický údaj, se kterým se dá pracovat. Jako druhou hlavní nevýhodu bych pak uvedl drobné kousání na telefonu. Je sice fajn, že služba mapuje i mobilní uživatele, jenom by to mohla provádět trochu rychleji.</p>
<h2>3. PTengine.com</h2>
<p><a class="potlaceno" href="img/2016/www/heatmaps/ptengine.com_sample.jpg" rel="lightbox"><img class="img-bordered" src="http://mike.treba.cz/img/2016/www/heatmaps/ptengine.com_preview.jpg" alt="ptengine.com_preview.jpg" /></a></p>
<p>Třetí nástroj mě příliš nepotěšil, proto ani já ho nepotěším ve svém srovnání. Založil jsem účet, přešel k zadání domény a tam jsem také skončil. Mé doménové jméno neprošlo verifikací. Že by mu vadila třetí úroveň? Na tuto otázku jsem odpověď nedostal... V nastavení nešlo nikde dohledat žádnou revalidaci, a tak jsem si pomyslel "<em>máte to rozbitý</em>" a šel jsem zkoušet dál.</p>
<p>Ovšem za pár dnů mi ve schránce přistál e-mail. Pán je prý z PTengine a rád by se se mnou spojil po Skypu, jakože probereme můj business plán nebo co. No milému pánovi jsem odepsal, že na PC nemám mikrofon, takže si moc nepokecáme, ale že bych ocenil krátké vysvětlení, co jsem při registraci udělal špatně. Že mi to nefrčí.</p>
<p>Odpověď samozřejmě žádná, ale po dalších pár dnech služba nejednou fungovat začala... Tak jo no. Každopádně PTengine ne. Máme lepší.</p>
<h2>4. Hotjar.com</h2>
<p><a class="potlaceno" href="img/2016/www/heatmaps/hotjar.com_sample.jpg" rel="lightbox"><img class="img-bordered" src="http://mike.treba.cz/img/2016/www/heatmaps/hotjar.com_preview.jpg" alt="hotjar.com_preview.jpg" /></a></p>
<p>Posledním testovaným subjektem byl <strong>Hotjar.com</strong>. Služba je mnohem jednodušší než předchozí Mouseflow, ale zase není nijak omezena. Stovkový limit se zde vztahuje pouze na videa, ale při jeho dosažení stačí uložená videa smazat a jedete nanovo. Měřicí kód stránky nezpomaluje, celkově to běží bez problémů. V administraci jsou také drobné nedostatky s vykreslením stránky, ale i tak... Hotjar je takový rozumný kompromis mezi cenou a výkonem a svůj účel rozhodně splní.</p>
<p>Takže tak. To by bylo ke srovnání asi vše. No nakonec jsem napsal článek mnohem kratší, než jsem původně čekal... Když vezmu v potaz vynaložené úsilí. Ale pár trefných slov je někdy více, než hromada odstavců vaty. Možná ještě jedna drobnost stojí za zmínku a tou je počet informačních emailů: u jednotlivých služeb jsem koukal i na to, jak moc mi spamují schránku. Nejvíc toho posílal asi Hotjar a PTengine... Jasně, ti, kterým to nefunguje, otravují nejvíc. Ale ve chvíli, kdy mám monitorování vypnuté, chodí třeba jeden email za měsíc, což není tak hrozné. Co jsem netestoval, byla technická podpora. Ono vlastně ani nebylo na co se ptát. Tedy jediný PTengine mě ignoroval, ostatní jsem neměl důvod kontaktovat.</p>
<p>Na úplný závěr ještě uvedu pár odkazů z českého internetu. Trochu delší povídání o heatmapách bylo publikováno na serveru <strong>Lupa.cz</strong>, ovšem většinou se hovoří o nástrojích placených. Což je také důvod, proč nemám ve svém srovnání uvedenou žádnou českou službu: je to všechno placené.</p>
<h4>Zajímavé články:</h4>
<ul>
<li><a href="http://www.paulolyslager.com/heatmap-hot-or-not/" target="_blank">Heatmap, Hot or Not? Free Alternatives for Crazy Egg</a></li>
<li><a href="http://www.lupa.cz/clanky/teplotni-mapy-porovnani-nastroju-a-studie/" target="_blank">Teplotní mapy pro web: porovnání nástrojů a k čemu slouží</a></li>
</ul>]]></content:encoded>
			<image>
    				<url>https://mike.treba.cz/img/2016/www/world_heatmap.png</url>
			</image>
		</item>
		
		
		<item>
			<title>YouTube videa: Postupné načítání při scrollu</title>
			<link>https://mike.treba.cz/youtube-videa-postupne-nacitani-pri-scrollu/</link>
			<pubDate>Tue, 10 May 2016 20:22:27 +1100</pubDate> 
			<comments>https://mike.treba.cz/youtube-videa-postupne-nacitani-pri-scrollu/#comments</comments>
			<dc:creator></dc:creator>
			<description><![CDATA[<p>Dnes si příklad z minulého článku rozšíříme o další funkcionalitu. Sám jsem dlouho hledal řešení, jak zamezit zbytečnému kousání stránek, které obsahují větší množství vložených videí. Chtěl jsem možnost přehrání uvnitř článku, ale už se mi tolik nelíbilo, jak se všechno musí dopředu zbytečně načítat. </p>]]></description>   
			<guid isPermaLink="false">https://mike.treba.cz/youtube-videa-postupne-nacitani-pri-scrollu/</guid>
			<content:encoded><![CDATA[<p>Dnes si příklad z minulého článku rozšíříme o další funkcionalitu. Sám jsem dlouho hledal řešení, jak zamezit zbytečnému kousání stránek, které obsahují větší množství vložených videí. Chtěl jsem možnost přehrání uvnitř článku, ale už se mi tolik nelíbilo, jak se všechno musí dopředu zbytečně načítat. </p> <p>Řešení je vcelku triviální a sám už jsem ho mnohokrát použil v jiných případech: daný obsah načteme až po zascrollování. Jak vše uvést do praxe vysvětlím právě v dnešním článku.</p>
<p>JavaScript si rozšíříme o jednu funkci. Upravíme i regulární výraz pro náhradu iframu vlastním blokem kódu. Iframe je totiž stylovatelný jako každý jiný prvek a toho využijeme. Opět musím zdůraznit, že by to šlo i jinak, přidáním dalších elementů, ale není to nutné. Můžeme využít to, co máme.</p>
<p>Základem je, aby se jednotlivým iframům vytiskl prázdný atribut "<strong>src</strong>". Dokud není zdroj, nic se nenačítá. Zdroj si dočasně uložíme do titulku, který potom přes JavaScript vyměníme. Atributy vyměníme ve chvíli, kdy uživatel na video zascrolluje.</p>
<h2>1. JavaScript</h2>
<p>Funkce je opět trochu zjednodušená: počítáme jen, jak daleko je zascrollováno. Pokud je video na stránce výš, než je spočtená hodnota, proběhne náhrada a jeho načtení. Toto řešení má výhodu v obrovské jednoduchosti, nevýhodu pak v tom, že když na konci stránky stiskneme F5, bude se načítat samozřejmě všechno. Ale o výpočtu viditelného okna z obou stran tento článek není a daná funkce pro svůj účel postačí.</p>
<p>Nesmíme ani zapomenout na její opětovné volání při resizu okna, protože u responzivních webů se hodnoty po resizu mohou (<em>a měly by</em>) změnit.</p>
<pre><code class="js">$.fn.showWhenScrolled=function(){
	var self = this;
	
	this.init = function(){
		self.each(function(){
			var self_top   = $(this).offset().top;
			var scroll_top = $(window).scrollTop();
			var win_height = $(window).height();
			var frame      = $(this).find('iframe');
			if(scroll_top &gt; (self_top - win_height)){
				if(!frame.attr('src')){
					frame.attr({src:frame.attr('title')});
				}
			}
		});
	};
	
	$(window).scroll(function(){
		self.init();
	}).resize(function(){
		self.init();
	});
	
	self.init();
};

$('.youtube-container').showWhenScrolled();</code></pre>
<h2>2. CSS</h2>
<p>Do stylu přidáme jediný řádek, a to vzhled prázdného rámu.</p>
<pre>.youtube-container iframe {background:rgba(0,0,0,0.25) url(youtube-loading.gif) no-repeat 50% 50%;}</pre>
<h2>3. HTML / PHP</h2>
<p>Na závěr musíme upravit i způsobe renderování, aby se mohl atribut "src" rovnou vyměnit s titulkem.</p>
<pre><code class="php">$youtube_replace_pattern = '
	&lt;span class="youtube-container"&gt;
		&lt;a href="javascript:;"&gt;
			&lt;img src="images/youtube-resizer.png" alt="" class="youtube-resizer" /&gt;
			&lt;img src="images/youtube-resizer-tablet.png" alt="" class="youtube-resizer tablet-resizer" /&gt;
		&lt;/a&gt;&lt;iframe $1 src="" title="$2" $3&gt;&lt;/iframe&gt;
	&lt;/span&gt;
';

$article_text = preg_replace('/\&lt;iframe([^&gt;]&plus;)src="([^"]*)"([^&gt;]&plus;)&gt;&lt;\/iframe&gt;/', $youtube_replace_pattern, $article_text);</code></pre>
<h2>Ukázka v praxi</h2>
<p>Jako ukázku jsem si připravil část jednoho ze svých článků. Zkuste si různě refreshovat, resizovat, scrollovat a uvidíte, jakým způsobem to všechno pracuje.</p>
<p class="download-links"><a href="doc/2016/youtube_load/index.html" target="_blank">YouTube progressive load ukázka</a><br /> <a href="doc/2016/youtube_load/youtube_load.zip" target="_blank">YouTube progressive load ke stažení</a></p>]]></content:encoded>
			<image>
    				<url>https://mike.treba.cz/img/2016/www/yutube-on-tablet.jpg</url>
			</image>
		</item>
		
		
		<item>
			<title>Jak na embedovaná YouTube videa: Responzivita</title>
			<link>https://mike.treba.cz/embedovana-youtube-videa-responzivita/</link>
			<pubDate>Sun, 10 Apr 2016 20:55:14 +1100</pubDate> 
			<comments>https://mike.treba.cz/embedovana-youtube-videa-responzivita/#comments</comments>
			<dc:creator></dc:creator>
			<description><![CDATA[<p>V předchozím dílu jsme si ukázali, jak získat náhledový obrázek YouTube videa. Dnes tuto znalost konečně zužitkujeme. V dalším ze série krátkých článků si ukážeme, jak snadno a jednoduše přizpůsobit inline rámy responzivnímu designu. </p>]]></description>   
			<guid isPermaLink="false">https://mike.treba.cz/embedovana-youtube-videa-responzivita/</guid>
			<content:encoded><![CDATA[<p>V předchozím dílu jsme si ukázali, jak získat náhledový obrázek YouTube videa. Dnes tuto znalost konečně zužitkujeme. V dalším ze série krátkých článků si ukážeme, jak snadno a jednoduše přizpůsobit inline rámy responzivnímu designu. </p> <p>Teoreticky půjde následující postup: iframe obalíme elementem, který dostane na pozadí náhledový obrázek. V něm se podle rozlišení zobrazí buď iframe nebo externím odkaz. Background-image containeru se taktéž zapne až při určitém rozlišení a/nebo zařízení.</p>
<p>Celá konstrukce bude mít ještě pár prvků navíc, aby nám objekt hezky držel rozměry, plus abychom měli kam dát ikonku symbolizující play. Držení poměru přes pomocné obrázky je jenom jedno z možných řešení, lze použít i jiné, akorát já si tento způsob oblíbil. Pro telefon bude box zobrazen v poměru 3:2, pro tablet potom 3:1, aby byl obrázek širší a nezabíral zbytečně moc místa.</p>
<h2>Zdroj</h2>
<p>Níže máme příklad videa tak, jak bude vložené ve stránce.</p>
<pre><code class="html">&lt;iframe width="420" height="315" src="//www.youtube.com/embed/uZtnaQm9xnk" frameborder="0" allowfullscreen&gt;&lt;/iframe&gt;</code></pre>
<h2>HTML / PHP</h2>
<p>Zdroj musíme překonvertovat do následující podoby. Tento kód můžeme buď vkládat přímo do článku nebo si ho až na výstupu přeformátovat. Toho docílíme velice snadno regulárním výrazem. Správně by měl být ještě trochu více konkrétní, ale pokud každý iframe v jakémkoli článku bude pouze YouTube, zápis musí stačit.</p>
<pre><code class="php">$youtube_replace_pattern = '
	&lt;span class="youtube-container"&gt;
		&lt;a href="javascript:;"&gt;
			&lt;img src="images/youtube-resizer.png" alt="" class="youtube-resizer" /&gt;
			&lt;img src="images/youtube-resizer-tablet.png" alt="" class="youtube-resizer tablet-resizer" /&gt;
		&lt;/a&gt;&lt;iframe $1&gt;&lt;/iframe&gt;
	&lt;/span&gt;
';</code></pre>
<h2>Regulární výraz</h2>
<p>Před náhradou ještě proženeme text článku funkcí strpos, abychom na to zbytečně nepouštěli preg_replace a tím nezpomalovali renderování stránky.</p>
<pre><code class="php">if(strpos($article_text,'&lt;iframe') !== false){
	$article_text = preg_replace('/&lt;iframe([^&gt;]&plus;)&gt;&lt;\/iframe&gt;/', $youtube_replace_pattern, $article_text);
}</code></pre>
<h2>CSS</h2>
<p>Dále musíme celý objekt řádně nastylovat. Container bude mít na pozadí právě získaný obrázek, pro rozlišení nad 641 pixelů vypnutý. Element odkazu pak piktogram pro přehrání, resizovací obrázek bude nastavený na 100% šířky. Nesmíme zapomenout ani na max-width u iframu.</p>
<p>Rozlišením pod 640 pixelů cílím telefony a úzká okna na desktopu. Pro tablety musíme styly trošičku ohnout, protože ty se rozlišením cílí docela špatně... Tablet určíme podle CSS třídy na &lt;body&gt;, takže jenom zopakujeme blok stylů pro telefon. Na dotykovém zařízení totiž chceme chování stejné.</p>
<p>Těch možností detekce a zápisu stylů je opět víc, já si zvykl na tento způsob. A jak dostat třídu "tablet" na &lt;body&gt; si ukážeme v jednom z dalších článků.</p>
<pre><code class="css">.youtube-container {display:block;position:relative;background:transparent none no-repeat 50% 50%;
	background-size:cover;-webkit-background-size:cover;
}
.youtube-container &gt; a {display:none;background:url(youtube-play.png) no-repeat 50% 50%;
	background-size:15% auto;-webkit-background-size:15% auto;
}
.youtube-container &gt; a &gt; img.youtube-resizer {display:none;margin:0;width:100%;max-width:100%;height:auto;position:relative;z-index:100;}
.youtube-container &gt; iframe {display:block;max-width:100%;}

@media all and (min-width: 641px){
	body:not(.tablet) .youtube-container {background-image:none !important;}
}

@media all and (max-width: 640px){
	.youtube-container {background-color:rgba(0,0,0,0.5);}
	.youtube-container &gt; a {display:block;}
	.youtube-container &gt; a &gt; img.youtube-resizer {display:block;}
	.youtube-container &gt; a &gt; img.youtube-resizer.tablet-resizer {display:none;}
	.youtube-container &gt; iframe {display:none;}
}

.tablet .youtube-container {background-color:rgba(0,0,0,0.5);}
.tablet .youtube-container &gt; a {display:block;}
.tablet .youtube-container &gt; a &gt; img.youtube-resizer {display:none;}
.tablet .youtube-container &gt; a &gt; img.youtube-resizer.tablet-resizer {display:block;}
.tablet .youtube-container &gt; iframe {display:none;}</code></pre>
<h2>JavaScript</h2>
<p>Na závěr potřebujeme ještě trochu JavaScriptu. Ten právě načte náhledový obrázek a také podle vzoru nastaví externí odkaz na video.</p>
<pre><code class="js">$.fn.youTubeImage = function(){
	this.each(function(){
		var src = $(this).find('iframe').attr('src');
		var arr = src.split('/');
		
		if(arr.length){
			var id = arr[arr.length-1].replace(/^(.*)\?(.*)$/, '$1');
			var image = 'http://img.youtube.com/vi/' &plus; id &plus; '/0.jpg';
			$(this).css({backgroundImage:'url(' &plus; image &plus; ')'});
		}
		
		var href = 'https://www.youtube.com/watch?v=' &plus; id;
		$(this).find('a').attr({href:href,target:'_blank'});
	});
};

$('.youtube-container').youTubeImage();</code></pre>
<h2>Ukázka v praxi</h2>
<p>Pro potřeby článku jsem si nachystal malou ukázku, kde se můžete podívat, jak se video změní v odkaz a naopak. Stačí hýbat s šířkou prohlížeče.</p>
<p class="download-links"><a href="doc/2016/youtube_images/index.html" target="_blank">YouTube responsive iframe ukázka</a><br /> <a href="doc/2016/youtube_images/youtube_images.zip" target="_blank">YouTube responsive iframe ke stažení</a></p>]]></content:encoded>
			<image>
    				<url>https://mike.treba.cz/img/2016/www/online-video-youtube-on-phone.jpg</url>
			</image>
		</item>
		
		
		<item>
			<title>Jak získat náhledový obrázek YouTube videa</title>
			<link>https://mike.treba.cz/jak-ziskat-nahledovy-obrazek-z-youtube/</link>
			<pubDate>Mon, 28 Mar 2016 20:21:07 +1100</pubDate> 
			<comments>https://mike.treba.cz/jak-ziskat-nahledovy-obrazek-z-youtube/#comments</comments>
			<dc:creator></dc:creator>
			<description><![CDATA[<p>V prvním ze série krátkých článků ukážu, jak se snadno dostat k náhledovému obrázku YouTube videa. Každý z mých textů nutně nemusí být dlouhý, navíc i mně se bude tato ukázka hodit jako zdroj informací pro článek další. Vím: dohledat řešení je otázka pár minut na Googlu, tak vám ho nabídnu alespoň v češtině. Člověk nikdy neví, kdy se něco takového může hodit.</p>]]></description>   
			<guid isPermaLink="false">https://mike.treba.cz/jak-ziskat-nahledovy-obrazek-z-youtube/</guid>
			<content:encoded><![CDATA[<p>V prvním ze série krátkých článků ukážu, jak se snadno dostat k náhledovému obrázku YouTube videa. Každý z mých textů nutně nemusí být dlouhý, navíc i mně se bude tato ukázka hodit jako zdroj informací pro článek další. Vím: dohledat řešení je otázka pár minut na Googlu, tak vám ho nabídnu alespoň v češtině. Člověk nikdy neví, kdy se něco takového může hodit.</p> <p>Každé video má 4 vygenerované náhledy očíslované od nuly do tří. <strong>Nultý náhled je vždy velký, 1-3 jsou malé</strong>.</p>
<pre><code>http://img.youtube.com/vi/<strong>{ID VIDEA}</strong>/0.jpg
http://img.youtube.com/vi/<strong>{ID VIDEA}</strong>/1.jpg
http://img.youtube.com/vi/<strong>{ID VIDEA}</strong>/2.jpg
http://img.youtube.com/vi/<strong>{ID VIDEA}</strong>/3.jpg</code></pre>
<p>Pokud si nejste jisti IDčkem videa, jen připomenu, že je obsaženo jak ve skutečné adrese, tak v odkazu na iframe.</p>
<pre><code>https://www.youtube.com/watch?v=<strong class="color-blue">0dFz10R529g</strong>
<iframe width="560" height="315" src="//www.youtube.com/embed/<strong class="color-blue">0dFz10R529g</strong>"></iframe></code></pre>
<p>YouTube ještě nabízí další volby náhledů různých kvalit, ovšem málo kdo tam ty obrázky fyzicky nahrává. Zpravidla se jedná o kopii některého z výše uvedených, takže si většinou musíme vystačit s hlavní miniaturou. Pro úplnost ale uvedu možnosti. (<em>Poslední dvě pak nemusí existovat.</em>)</p>
<pre><code><em class="color-grey">// výchozí</em>
http://img.youtube.com/vi/<strong>{ID VIDEA}</strong>/default.jpg

<em class="color-grey">// vysoká kvalita</em>
http://img.youtube.com/vi/<strong>{ID VIDEA}</strong>/hqdefault.jpg

<em class="color-grey">// střední kvalita</em>
http://img.youtube.com/vi/<strong>{ID VIDEA}</strong>/mqdefault.jpg

<em class="color-grey">// standardní kvalita</em>
http://img.youtube.com/vi/<strong>{ID VIDEA}</strong>/sddefault.jpg

<em class="color-grey">// maximální možné rozlišení</em>
http://img.youtube.com/vi/<strong>{ID VIDEA}</strong>/maxresdefault.jpg</code></pre>
<p>Nejčastěji tedy využijeme náhled 0. Jinak podrobnější popis jednotlivých thumbnailů si můžete přečíst v původním článku na <a href="http://stackoverflow.com/questions/2068344/how-do-i-get-a-youtube-video-thumbnail-from-the-youtube-api" target="_blank">Stackoverflow</a> v angličtině.</p>
<p>Tak. A máme libovolnou verzi náhledu. Využití v praxi ukážu v dalším článku.</p>]]></content:encoded>
			<image>
    				<url>https://mike.treba.cz/img/2016/www/yt-big.jpg</url>
			</image>
		</item>
		
	
</channel>
</rss>