Jak poprawnie budować rozwijaną nawigację

Po pierwsze – rozwijane menu nawigacyjne były złym pomysłem 10 lat temu i są tym bardziej złym pomysłem teraz. Kto próbował użyć takiej nawigacji na ekranie dotykowym, gdzie nie istnieje koncepcja :hover, ten świetnie wie o czym mówię. Chciałem to zaznaczyć na samym początku – jeśli możecie, nie używajcie w ogóle menu rozwijanych!

Do rzeczy. Ten wpis nie ma na celu szczegółowego omówienia technik budowania rozwijanych menu nawigacyjnych. Chcę tylko zwrócić uwagę na jeden konkretny aspekt.

Problem – display: none;

Bardzo często widzę, taką strukturę CSSa do ukrywania/chowania menu.

#nav li { float: left; position: relative; height: 30px; }
#nav li ul { display: none; position: absolute; top: 30px; left: 0; }
#nav li:hover ul { display: block; }

To działa, ale ma jedną zasadniczą wadę – domyślnie wszystkie linki w podmenu są ukryte przed wyszukiwarkami, czytnikami ekranu itd. Według mnie display: none; powinno być tylko używane w połączeniu z JavaScriptem, o ile chcemy zachować dostępność treści.

Rozwiązanie: left: -999em;

Lepszym rozwiązaniem jest użyć minusowego pozycjonowania elementów:

#nav li ul { position: absolute; top: 30px; left: -999em; }
#nav li:hover ul { left: 0; }

Efekt taki sam, a nagle i roboty będą łapać Wasze linki i użytkownicy korzystający z klawiatury będa mogli nawigować po waszej stronie (przynajmniej w teorii, w praktyce wymaga to jeszcze trochę zachodu, żeby uczynić rozwijaną nawigację w pełni dostępną z klawiatury).

Posted in CSS

SQL Buddy – alternatywa dla phpMyAdmina

Michał dał mi namiary na naprawdę niezłego manadżera baz MySQL – SQL Buddy. Jest napisany w PHP, podobnie jak wskazany w tytule phpMyAdmin. Jest jednak kilka reczy różniących te dwa programy, które moim zdaniem przemawiają na rzecz SQL Buddy:

  • Aplikacja wykorzystuje sporo JavaScriptu – dzięki temu wrażenia z pracy są bardziej zbliżone do aplikacji desktopowej niż do korzystania ze statycznej strony internetowej.
  • Nie wymaga instalacji ani konfiguracji.
  • Paczka ze skryptami ma około 165KB (gdy sprawdzałem ostatnią wersję phpMyAdmina było to bliżej 3MB).
  • Sam wygląd jest prosty, przejrzysty i intuicyjny – zdecydowanie bardziej użyteczny niż dość szpetny z natury phpMyAdmin.
  • Nie jest przeładowany zbędnymi funkcjami – porównajcie chociażby ekrany importu i eksportu. phpMyAdmin obsługuje o wiele więcej formatów danych, jednak jak często potrzebujecie czegoś innego niż SQL? Ja osobiście niezbyt często, choć oczywiście zdarzyło mi się korzystać kilkukrotnie z plików CSV.

Nie zrozumcie mnie źle – pMA to bardzo dobry program – nie bez powodu jest tak popularny. Uważam jednak, że SQL Buddy to bardzo zgrabna alternatywa, która w zupełności wystarcza do codziennej pracy.

Nowa odsłona IE PNG Fix

IE6 nie wybiera się na zasłużoną emeryturę w najbliższej przyszłości. Codzienna udręka z tą przeglądarką stała się właśnie odrobinę wygodniejsza.

Powstała nowa wersja popularnego narzędzia IE PNG Fix, autorstwa Twin Helix Design. Służy ono do włączania obsługi kanałów alpha w obrazkach zapisanych w formacie PNG.

Nowa wersja 2.0 alpha 1 ma dwie nowe, rewelacyjne funkcje, niedostępne wg mojej wiedzy w innych rozwiązaniach. Udostępniono obsługę dwóch właściwości CSS: background-position i background-repeat. Mam szczerą nadzieję, że ten skrypt podoła ciężkim warunkom bojowym codziennej pracy i będzie można go swobodnie używać do momentu, gdy IE6 przestanie mieć znaczenie (koło 2011 roku, jak tak dalej pójdzie z tempem konwersji).

Kontrolki ActiveX w IE wracją do poprzedniego stanu

Od kwietnia 2008 roku zniknie potrzeba aktywowania kontrolek w IE! Po długiej walce Microsoftowi udało się odkupić od firmy Eolas licencję na technologię, która to umożliwia. Wszyscy twórcy stron/aplikacji flashowych będą mieli jeden problem mniej z głowy.

Z drugiej strony problem można było obejść korzystając z takich technik jak UFO czy SWFObject. Zawsze wydawało mi się to lepszym rozwiązaniem.

XHTML2 a HTML5

David „liorean” Andersson opublikował w portalu Digital Web Magazine ciekawy artykuł “HTML5, XHTML2, and the Future of the Web”, pokazujący bardzo trafnie różnice między XHTML2 i HTML5.

Autor nie ukrywa też swoich preferencji co do dalszego rozwoju języka HTML:

HTML5 will be the future of the web, so my advice would be to pay close attention to it.

Pogląd ten jest poparty kilkoma dobrymi argumentami, więc sądzę że warto zapoznać się z całym tekstem, jeśli jeszcze nie wyrobiliście sobie zdania w tej kwestii.

Prezentacja Rogera Costello na temat hCard

Szybkie wprowadzenie – mikroformaty pozwalają na wzbogacenie znaczenia danego kodu HTML lub XML poprzez dodanie do niego określonych, ustandaryzowanych klas. Dzięki temu możemy w prosty sposób zapisywać informacje o osobach, wydarzeniach, położeniu, itd. Głównym źródłem informacji na ten temat powinna być strona microformats.org, jednak większość zamieszczonych tam danych to techniczne specyfikacje.

Jestem osobiście bardzo zainteresowany tym tematemi już kilkukrotnie podejmowałem próbę wdrożenia któregoś z mikroformatów w mojej pracy. Niestety efekty były raczej dalekie od satysfakcjonujących. Po pierwsze wynikało to z braku odpowiednich narzędzi do weryfikowania poprawności stworzonego kodu, a po drugie z niedostatecznej ilości informacji i przykładów w specyfikacji.

Wczoraj jednak przeczytałem prezentację na temat formatu hCard, której autorem jest Roger Costello. Podzielona na pięć części w bardzo szczegółowy sposób opisuje wszystkie aspekty poszczególnych sekcji hCard, takich jak dane osobiste, adres, kontakt, stanowisko, organizacja, itd.. Szczerze polecam lekturę – w 30 minut nauczyłem się więcej niż w trakcie kilku godzin studiowania przykładów na microformats.org.

Label jako komunikat błędu

Zastanawiałem się ostatnio nad wykorzystaniem elementu label do oznaczania elementów błędu. Według specyfikacji ze stron W3C:

The LABEL element may be used to attach information to controls. Each LABEL element is associated with exactly one form control.

Uważam, że komunikat błędu można jak najbardziej podciągnąć pod tą definicję. Jest przecież bezpośrednio powiązany z daną kontrolką. Pytanie brzmi – jakie są korzyści takiego rozwiązania? Rozpatrzmy następujący formularz:

<form method="post" action="index.html">
  <div id="errors">
    <h2>Błędy</h2>
    <ul>
      <li><label for="name"><em>Imię i nazwisko</em>: Proszę podać imię i nazwisko</label></li>
      <li><label for="email"><em>Email</em>: Podany adres e-mail jest nieprawidłowy</label></li>
    </ul>
  </div>
  <div><label for="name">Imię i nazwisko <em>(wymagane)</em></label> <input type="text" name="name" id="name" value=""> <strong class="error"><label for="name">Proszę podać imię i nazwisko</label></strong></div>
  <div><label for="email">Adres e-mail <em>(wymagany)</em></label> <input type="text" name="email" id="email" value="Lorem ipsum"> <strong class="error"><label for="email">Podany adres e-mail jest nieprawidłowy</label></strong></div>
  <div><input type="submit" value="Wyślij formularz"></div>
</form>

Powyższy przykład na żywo
Takie rozwiązanie przypisuje bezpośrednio informację o popełnionym błędzie do danego pola formularza. Według mnie może to być bardzo istotne zwłaszcza przy długich formularzach, występujących poniżej linii zgięcia. Po przeładowaniu strony nie widzimy bezpośrednio formularza ani komunikatów błędu z nim powiązanych. Świadczy to przeważnie o niewłaściwym projekcie strony, ale przykra rzeczywistość jest taka, że od czasu do czasu musimy borykać się z tego typu problemami.

Moje pytanie brzmi – czy takie rozwiązanie jest prawidłowe (nie spotkałem się z nim na innych stronach) i czy przynosi użytkownikom jakąkolwiek korzyść? Osobiście zauważyłem już jeden problem. Kliknięcie na label w górnej liście błędów przenosi do odpowiedniego pola, ustawiając na nim focus. Nie ma jednak powrotu do listy błędów, i efekt może być taki, że znajdziemy się gdzieś w połowie formularza nie będąc jednocześnie całkowicie świadomym położenia swojego i innych pól, w których wystąpiły błędy.

Czuję, że ta technika ma przyszłość, ale obecnie nie jestem do końca pewien, jak to rozwiązać przy pomocy czystego HTML. Będę wdzięczny za wszelkie uwagi.

Firebug 1.0 beta

Ukazała się wersja 1.0 beta wtyczki Firebug dla przeglądarki Firefox. Szczerze muszę przyznać – przerosła moje najśmielsze oczekiwania. Poniżej lista kilku cech, które dosłownie wgniotły mnie w fotel:

  • Zakładka Net, na której w czasie rzeczywistym możemy śledzić prędkość pobierania poszczególnych plików, wszystkie nagłówki HTTP (w tym wyróżnione ewentualne błędy 404), a także filtrować informacje o pobieranych plikach według ich typu.
  • Widok drzewa HTML wraz ze stylami CSS przypisanymi bezpośrednio do poszczególnych elementów oraz dziedziczonymi z poprzednich elementów
  • Zakładka Layout, która pokazuje graficzną reprezentację tzw. box model, czyli rozmiaru, marginesów, krawędzi itd.
  • Funkcja Profile w konsoli do sprawdzania czasu wykonywania poszczególnych funkcji.

Sam już nie wiem, czy znalazłem wszystkie opcje tego rewelacyjnego narzędzia. To jednak nie wszystko! Joe Hewitt dodatkowo udostępnił skrypt Firebug Lite, który daje dowolnej przeglądarce możliwość logowania informacji do konsoli Firebuga, tak jak było to do tej pory możliwe tylko w Firefoxie.

Śmiało mogę powiedzieć, że to najlepsze narzędzie do tworzenia stron WWW od czasu pierwszego wydania Web Developera.

Autor zastanawiał się nad zmianą licencji na komercyjną, ale postanowił jednak, że FB pozostanie darmowy. Jeśli macie taką możliwość – wyślijcie mu kilka złotych. Szybko je zaoszczędzicie z tym świetnym rozszerzeniem…

Smooth Slideshow autorstwa JonDesign

Można odnieść wrażenie, że nie zajmuję się niczym innym poza galeriami i pokazami slajdów, ale trafiłem na kolejne świetne i godne zainteresowania rozwiązanie.

Smooth Slideshow to darmowa biblioteka, autorem której jest Jonathan Schemoul. Kilka słów podsumowania:

  1. pozwala na wyświetlanie zdjęć w trybach zarówno ręcznym jak i automatycznym,
  2. jest oparta o framework mootools,
  3. skrypty ważą razem około 22kB.

I jeszcze dwa linki bezpośrednio do przykładów:

TripTracker slideshow – Lightbox na sterydach

W jednym z ostatnich projektów klient zażyczył sobie galerii zbliżonej w działaniu do Lightboxa, ale dodatkowo wzbogaconej o funkcję pokazu slajdów. Zgodnie z podstawową zasadą leniwego programisty zacząłem od przeszukania zasobów Internetu w celu znalezienia gotowego rozwiązania. W krótkim czasie trafiłem tutaj: Revamped Slideshow, na blogu TripTracker.

Klient wyraził swoją aprobatę, więc pozostało jedynie zaimplementować to ustrojstwo na stronie, a właściwie zastąpić obecnego już Thickboxa. Niestety – oferowany przez autorów sposób integracji jest dość nieszczęśliwy i całkowicie niezgodny z zasadami unobtrusive javascript [ciągle nie znam dobrego polskiego tłumaczenia]. Dlatego przygotowałem krótki skrypt, który załatwia to w bardziej elegancki sposób.

Wersja zależna od jQuery

var uoSlideshow = {
	CLASS_NAME : 'slideshow',
	viewer : new PhotoViewer(),
	initialize : function(){
		$('a.'+uoSlideshow.CLASS_NAME).each(function(i){
			uoSlideshow.viewer.add(this.href);
			$(this).click(function(e){ e.preventDefault(); uoSlideshow.viewer.show(i)});
		});
	}
}

$(document).ready(uoSlideshow.initialize);

Wersja w osobnym pliku | Przykład

Powyższe skrypt wymaga podłączonej biblioteki jQuery. Aby wszystko działało poprawnie powyższy plik powinnien być podłączony po plikach jQuery i właściwego slideshowa, czyli np w ten sposób:

<script type="text/javascript" src="js/jquery.js"></script>
<script type="text/javascript" src="js/slide.js"></script>
<script type="text/javascript" src="js/uoSlideshow-jquery.js"></script>

Wersja niezależna od zewnętrznych bibliotek

Napisałem też wersję, która może działać samodzielnie. Do przypisywania zdarzeń wykorzystałem funkcję addEvent() autorstwa Johna Resiga.

function addEvent( obj, type, fn )
{
	if (obj.addEventListener)
		obj.addEventListener( type, fn, false );
	else if (obj.attachEvent)
	{
		obj["e"+type+fn] = fn;
		obj[type+fn] = function() { obj["e"+type+fn]( window.event ); }
		obj.attachEvent( "on"+type, obj[type+fn] );
	}
}

function stopDefault(e){
	if (e.preventDefault) {
		e.preventDefault(); // The W3C DOM way
	} else {
		e.returnValue = false; // The IE way
	}
}

var uoSlideshow = {
	CLASS_NAME : 'slideshow',
	viewer : new PhotoViewer(),
	initialize : function(){
		for(var i=0; (a = document.getElementsByTagName('a')[i]);i++ ){
			if (uoSlideshow.isSlide(a)) {
				a.index = i;
				uoSlideshow.viewer.add(a.href);
				addEvent(a,'click',function(e){ stopDefault(e); uoSlideshow.viewer.show(this.index);  });
			}
		}
	},
	isSlide : function(obj)	{
		if (obj && obj.className){
			var cn = obj.className.split(" ");
			var i = cn.length-1;
			while (c = cn[i]) {
				if (c == uoSlideshow.CLASS_NAME){
					return true;
				}
				i--;
			}
		}
		return false;
	}
}

addEvent(window,'load',uoSlideshow.initialize);

Wersja w osobnym pliku | Przykład

Tutaj implementacja ogranicza się do wklejenia powyższego kodu w nagłówku:

<script type="text/javascript" src="js/slide.js"></script>
<script type="text/javascript" src="js/uoSlideshow-standalone.js"></script>