Jak wiadomo AJAX w systemie nawigacyjnym strony nie jest zalecany, ze względu na to że roboty wyszukiwarek takich jak Google mają problemy w poradzeniu sobie z indeksacją linków zapisanych jako kod JavaScript. Przez to automatycznie spada nasza pozycja w wynikach wyszukiwarek, większość treści naszej strony jest poza zasięgiem robotów i w konsekwencji mniej internautów do nas dociera. Jednak jest na to sposób, możemy nie ingerując w kod odnośnika do obsługi zdarzenia kliknięcia wykorzystać bezpośrednio JavaScript. W ten sposób zachowamy dobre zasady technik SEO. Dodatkowo nasz serwis będzie generować identyczne strony na przeglądarkach z włączonym lub wyłączonym JavaScript, z wł/wył ActiveX (przeglądarki IE), bez żadnych komunikatów o błędach. Czyli niezależnie czy klient obsługuje AJAX czy nie, uzyskamy bardzo podobne rezultaty. Oczywiście przeglądanie serwisu z włączonym JS (i w IE ActiveX) zdecydowanie poprawi szybkość i dynamiczność strony, ale odwrotna sytuacja wcale nie zakłóci pracy serwisu, a już na pewno, co wydaje się być najważniejsze, żaden z przypadków nie zakłóci indeksacji podstron przez wyszukiwarki. Wychodząc z zasady, że nie warto tworzyć czegoś co już istnieje, wykorzystamy skrypt JS AddEvent Manager autorstwa Angus Turnbull - www.twinhelix.com do obsługi zdarzeń. Skrypt korzysta z metod DOM takich jak addEventListener i removeEventListener, dzięki temu sami możemy tworzyć obsługę takich zdarzeń jak klikanie (co nas interesuje). Dodatkowo skrypt jest małych rozmiarów (około 1,4KB), więc można go zamieścić w kodzie źródłowym strony, lub dodać do jakiegoś naszego skryptu zewnetrznego. Raczej nie opłaca się tworzyć osobnego pliku (ze względu na “zaśmiecanie”). Autor skryptu udostępnia na swojej stronie również HTMLHttpRequest, demo tego skryptu działa podobnie do tego co chcemy osiągnąć w tym artykule, jednak na nasze potrzeby jest to rozwiązanie za mało elastyczne i niestety dodatkowo potrafi wykorzystywać pływające ramki, których my zamierzamy uniknąć. Dlatego do komunikacji z serwerem wykorzystamy inne rozwiązanie. Na razie jednak przyjrzyjmy się AddEvent Manager:
/*
AddEvent Manager (c) 2005-2006 Angus Turnbull http://www.twinhelix.com
Free usage permitted as long as this credit notice remains intact.
*/
if (typeof addEvent != 'function')
{
var addEvent = function(o, t, f, l)
{
var d = 'addEventListener', n = 'on' + t, rO = o, rT = t, rF = f, rL = l;
if (o[d] && !l) return o[d](t, f, false);
if (!o._evts) o._evts = {};
if (!o._evts[t])
{
o._evts[t] = o[n] ? { b: o[n] } : {};
o[n] = new Function('e',
'var r = true, o = this, a = o._evts["' + t + '"], i; for (i in a) {' +
'o._f = a[i]; r = o._f(e||window.event) != false && r; o._f = null;' +
'} return r');
if (t != 'unload') addEvent(window, 'unload', function() {
removeEvent(rO, rT, rF, rL);
});
}
if (!f._i) f._i = addEvent._i++;
o._evts[t][f._i] = f;
};
addEvent._i = 1;
var removeEvent = function(o, t, f, l)
{
var d = 'removeEventListener';
if (o[d] && !l) return o[d](t, f, false);
if (o._evts && o._evts[t] && f._i) delete o._evts[t][f._i];
};
}
// Optional cancelEvent() function you can call within your event handlers to
// stop them performing the normal browser action or kill the event entirely.
// Pass an event object, and the second "c" parameter cancels event bubbling.
function cancelEvent(e, c)
{
e.returnValue = false;
if (e.preventDefault) e.preventDefault();
if (c)
{
e.cancelBubble = true;
if (e.stopPropagation) e.stopPropagation();
}
};
Nie będe zagłębiał się w omówienie tych metod, bo nie to stanowi treść tego artykułu. Dlatego od razu przechodzimy dalej. Tworzymy serce naszego systemu czyli pliki nagłówka - inc.header.php i stopki - inc.footer.php. Dodatkowo plik index.php, który po prostu inkluduje nagłówek i stopkę oraz pliki w których będziemy przechowywać naszą treść czyli: aktualnosci.php, o_nas.php i kontakt.php. Oczywiście w realiach wcale nie musimy tworzyć takich plików możemy posłużyć się jednym plikiem index.php i tam zarządzać całą treścią pobieraną np. z bazy danych, wykorzystując czyste URLe po przez odpowiednie ustawienie serwera WWW (mod_rewrite). Na potrzeby artykułu pozostaniemy jednak przy takiej konfiguracji. Zobaczmy jak wyglądają poszczególne pliki.
<?php
include_once ("inc.header.php");
?>
<div id="targetArea"></div>
<?php
include_once ("inc.footer.php");
?>
<?php
header("Content-Type: text/html;charset=utf-8"); //niestety na Studencie bez tego wystepuja krzaczki
?><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>Demonstracja AJAX w nawigacji WWW</title>
<script type="text/javascript" src="./emg.js"></script>
<script type="text/javascript" src="./advajax.js"></script>
<script type="text/javascript">//<![CDATA[
//Funkcja korzystając z AdvancedAJAX do obsługi AJAX wczytuje dane do warstwy
function loadInto(src, destId, evt)
{
//Funkcja ta jest wywoływana w chwili gdy link klasy LinkAJAX-WarstwaDocelowa zostanie
//kliknięty. src to referencja do linku, destId docelowa warstwa, evt objekt zdarzenia
link_href=(src.href || src.getAttribute('href'));
//Wykorzorzystujemy skrypt advAJAX więcej na stronie http://advajax.anakin.us/
advAJAX.get({
url: link_href,
parameters : { //parametr posłuży do identyfikacji czy mamy dotrzynienia z AJAX
"AJAXOK" : "1"
},
onSuccess : function(obj) { //ładujemy stronę do warstwy
document.getElementById(destId).innerHTML=obj.responseText;
},
onError : function(obj) { //wyświetlamy błąd
document.getElementById(destId).innerHTML="Błąd "+obj.status;
}
});
//Ponieważ zapytanie działa asynchronicznie, nie jesteśmy w stanie
//zablokować przeładowania strony reagując na to czy element został
//załadowany poprawnie, czy też na to czy AJAX działa.
//Jeśli można obsłużyć AJAX to blokujemy przeładowanie strony (normalne
//zdarzenie na kliknięcie w link).
if (window.XMLHttpRequest || window.ActiveXObject) cancelEvent(evt);
};
//Dodajemy obsługę zdarzenia. Dzięki takiemu podejściu
//nie musimy deklarować onLoad w znaczniku body. Obsługa
//zostanie załadowana w czasie wczytywania strony.
addEvent(document, 'click', function(evt)
{
//Skanujemy wszystkie kliknięcia w obrębie dokumentu w celu odnalezienia linka
//w znaczniku a, ktorego klasa jest rowna LinkAJAX-(.+)
evt = evt || window.event;
//Sprawdzamy jedynie kliknięcia lewym klawiszem myszy.
//Jeśli napotkamy prawy to kończymy obsługę.
if (evt.which > 1 || evt.button > 1) return;
var src = evt.target || evt.srcElement;
//Ponieważ elementy mogą być zagnieżdzone, szukamy rodzica.
if (src.nodeType && src.nodeType != 1) src = src.parentNode;
//Skanujemy całe drzewo elementów DOM w poszukiwaniu tego pasującego
while (src)
{
var srcName = (src.nodeName||src.tagName||'').toLowerCase();
//Jesli znajdziemy element o znaczniku a, sprawdzamy czy jest klasy LinkAJAX-(.+)
if (srcName == 'a' && src.className && src.className.match(/^LinkAJAX-(.+)$/))
{
//Znaleźliśmy pasujący element więc wywołujemy funkcję obsługi i kończymy pętle.
return loadInto(src, RegExp.$1, evt);
}
//Jeśli nie znaleźliśmy pasującego elementu, przesuwamy się w górę drzewa.
src = src.parentNode;
}
}, 1);
//]]></script>
</head>
<body>
<div>Górna część strony</div>
<br />
<hr />
<div>
<a class="LinkAJAX-targetArea" href="./aktualnosci.php">Aktualności</a> -
<a class="LinkAJAX-targetArea" href="./o_nas.php">O nas</a> -
<a class="LinkAJAX-targetArea" href="./kontakt.php">Kontakt</a> -
<a class="LinkAJAX-targetArea" href="./nie_dzialajacy_link.php">Nie działający link</a>
</div>
<hr />
<br />
<br /><hr />
<div>Dolna część strony</div>
</body>
</html>
Mimo, że nagłówek jest dość dobrze opisany postaram się wyjaśnić kilka rzeczy. Korzystamy z kodowania znaków UTF-8 gdyż taka jest ostatnio tendencja, zresztą całkowicie uzasadniona. Do obsługi AJAXa wykorzystujemy bardzo fajny skrypt którego autorem jest Łukasz Lach, mianowicie AdvancedAJAX, opis na stronie http://advajax.anakin.us/, po zapoznaniu z dokumentacją gwarantuję że się spodoba
. Dodajemy obsługę zdarzenia “click”, filtrujemy wszystkie kliknięcia. Jeśli natrafimy na nasz element skojarzony z AJAXem po przez nazwę klasy znacznika “a”, mianowicie musi być to klasa o nazwie LinkAJAX-WarstwaDocelowa wywołana zostanie funkcja loadInto(), która korzystając z advAJAX wczyta żądaną stonę określoną w atrybucie “href” znacznika “a”, dodając do niej parametr AJAXOK=1. Ponieważ advAJAX działa asynchronicznie, nie jesteśmy w stanie zablokować przeładowania strony w reakcji na to czy skrypt załadował treść, dlatego sprawdzamy po prostu czy klient może obsłużyć AJAX. Jeśli tak to blokujemy przeładowanie strony. Jeśli nie to pozwalamy na przeładowanie (tak jak by w ogóle tego JavaScript’u nie było). Każda z podstron wygląda podobnie, dlatego pokażę kod jednej z nich.
<?php
if(!$_GET['AJAXOK']) {
include_once("inc.header.php");
echo '<div id="targetArea">';
}else header("Content-Type: text/html;charset=utf-8"); //niestety na Studencie bez tego wystepuja krzaczki
?>
<h1>Kontakt</h1><br />
Informacje kontaktowe<br />
ble ble ble<br />
ble <br />
ble ble ble<br />
ble ble ble ble<br />
<?php
if(!$_GET['AJAXOK']) {
echo '</div>';
include_once("inc.footer.php");
}
?>
Jak widać do pliku dołączany jest nagłówek i stopka jeśli w żadaniu GET nie występuje parametr AJAXOK - czyli jeśli treść nie jest pobierana za pośrednictwem AJAX, co powoduje że strona w każdym przypadku wygląda tak samo. I to jest cała filozofia działania. Jak się przekonamy jeśli klient korzysta z AJAX to możemy również w bardzo ładny sposób obsługiwać i reagować na błędy HTTP (np. pisząc dodatkową funkcję JS do obsługi błędów o konkretnych numerach), co pokazane jest w demonstracji, bez AJAX nie moglibyśmy tak reagować. Wystarczy pobawić się poziomem bezpieczeństwa w Internet Explorer (jeśli ktoś posiada
) i na pewno zauważymy różnice. Dzięki advAJAX możemy też w łatwy sposób np. informować, że właśnie ładujemy jakieś dane (przydatne przy cięższych plikach) - po szczegóły odsyłam do dokumentacji na stronie skryptu. Zapraszam do eksperymentowania.
DEMONSTRACJA
AJAX w nawigacji WWW (ZIP)
Data: 30 sierpień 2006 godzina: 23:51 Kategoria Programowanie WWW, Tutorials, SEO.
Możesz śledzić odpowiedzi za pomocą RSS 2.0.
Możesz zostawić komentarz, lub ślad na swojej stronie. Możesz też dodać ten wpis do swoich ulubionych social bookmarków.
5 wrz 2006 @ 11:22
Nameczyles sie. Ale wszystko napisane przejrzyscie i przystepnie.
Komentarzy malo ale podejrzewam, ze wiecej czytelnikow mogl bys zdobyc majac bloga w jakims blogowym serwisie (moze jakis techblogowy) a nie u siebie na koncie.
5 wrz 2006 @ 22:35
No bez przesady blogi !!! Trzeba szukać interesujących rzeczy i je znajdować ,szczerze jak widzę w linku coś co wszuje na bloga to nie wchodzę
5 wrz 2006 @ 22:46
W zasadzie nie promowałem tego bloga i nie zamierzam. Najpierw chce uzbierać trochę wpisów i zmienić adres na krótszy. Blogroll i wpisy do katalogów na pewno sporo dadzą, ale jeszcze czas na to. Poza tym cieżko mi określić kategorię tego bloga, bo w zasadzie pisze o swoich zainteresowaniach, a te są bardzo rozległe - jeszcze kilka działów na pewno się pojawi.
19 wrz 2006 @ 11:27
Fajna sprawa z tym menu. Na pewno wypróbuję.
12 paź 2006 @ 16:42
A jakby jeszcze jako dodatek pojawiło się omówienie tego emg.js to już w ogóle byłoby super
17 lis 2008 @ 10:30
Szkoda tylko, że przy przesyłaniu linków np. osobie do danego artykułu lub podstrony np. kontakt to i tak znajdziemy się na stronie głównej. Czy jest sposób, aby rozwiązać ten problem?
27 lis 2008 @ 00:21
Jeśli chodzi o podmianę adresu w pasku URL - nie ma takiej opcji.
Widzę 2 rozwiązania:
Stworzyć użyteczną opcję prześlij link/stronę znajomemu, kopiuj adres czy coś takiego i tam adres zawarty w href linku.
Wykorzystać kotwice czyli “#kotwica” w linku w href. Tylko tutaj obsługa musiałaby być zapewniona przez javascript, ponieważ sama kotwica nie jest przekazywana w żądaniu na serwer. Poza tym wymagałoby to wprowadzenia dalszych rozwiązań (np. takich żeby link dalej był indeksowany - blokowanie żądania kliknięcia, podmiana adresu linku na kotwice, wymuszenie żądania już z kotwicą).
19 maj 2009 @ 21:46
Jeszcze jakbyś powiedział jak zrobić, żeby na początku automatycznie ładował się jakiś plik jako start byłoby bardzo fajnie
7 cze 2010 @ 12:34
A co zobić żeby inne skrypty jquery np do menu działały na stronie, zbudowanej na tej podstawie, na ie wszystko ok zas na firefoksie oraz na operze menu nie działa….
7 cze 2010 @ 18:09
Przede wszystkim unikamy konfliktów http://docs.jquery.com/Using_jQuery_with_Other_Libraries
a jak juz korzystamy z jQuery, to mozemy sobie darowac rozwiazania z 2006 roku
Wyrzucamy skrypty JS podane wyżej, a skrypt w sekcji head zastepujemy:
i to wszystko.
Oczywiście pamiętamy o dołączeniu biblioteki jQuery.
8 cze 2010 @ 13:14
Hej !!
Dzięki za pomoc, wszystko działa super:)
21 cze 2010 @ 22:13
Czy jest jakis sposob, aby juz domyslnie jedna z podstron byla wczytana? Tzn. przypuscmy ze standardowo otwierajac stronke byla wczytana juz podstrona Aktualnosci i po kliknieciu na inny link w menu zmieniala sie zawartosc?
21 cze 2010 @ 22:38
Nie wiem czy o to chodzi. Po pierwsze mozna w warstwie docelowej (DIV) wpisac cokolwiek, bedzie widoczne dopoki nie kliknie sie na linki.
Po drugie mozna tez po zaladowaniu dokumentu, do warstwy docelowej wpisac dowolna tresc korzystajac z AJAXa, wystarczy dodac linijke do bloku
$(document).ready(function() { loadInto("aktualnosci.html", "#targetArea"); });21 cze 2010 @ 22:39
dzieki wielkie:)