В начале
В данной статье речь пойдет о написании Ajax-приложения. Если говорить проще — то, о написании сайта – работающего без перезагрузок. Быстро, легко, доступно. В этой статье не будет рассматриваться код серверной стороны, будут только примеры, для лучшего понимания.
Меня давно интриговала тема написания сайта, в котором несколько компонентов (например, flash плееры) не перезагружаются с каждым переходом по ссылкам, а продолжают себе напевать песенки. И вот однажды, набравшись смелости — я начал думать насчет структуры такого вот приложения. Что в итоге получилось — читайте ниже.
Начнемс
Для начала нам потребуется несколько js-плагинов – а именно:
Первый, это знакомый
Для начала создадим папку, например jstemp — в этой папке будут находится шаблоны jquery-tmpl. Еще нужно создать хотя бы один шаблон, например — шаблон главной страницы. Создаем в папке jstemp подпапку main в которой будет файл page.html
В файле простой html код:
<div id="name_of_page">${Content.name}</div>
<div id="info_of_page">${Content.info}</div>
Суть работы
В тот момент, когда пользователь заходит на страницу, наше ajax приложение обращается к серверу, с текущей ссылкой, и параметрами, и ждет в ответ JSON. Полученный JSON мы парсим в js-объект и отправляем плагину jquery-tmpl, который расставляет данные из объекта по полочкам, и показывает нужный шаблон нашему пользователю. А так же, если пользователь решит, что текущая страница ему не нужна, и попробует перейти на другую — ajax-приложение тут же отловит его действие и сделает те же самые процедуры, только уже с новой ссылкой, по которой юзер хотел перейти, и вернет результат работы.
Реализация
Для начала напишем начальную страницу:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<script language="Javascript" src="js/jquery-1.5.min.js"></script>
<script language="JavaScript" src="js/jquery.tmpl.js"></script> <!--Шаблонизатор-->
<script language="JavaScript" src="js/jquery.tmplPlus.min.js"></script> <!--Шаблонизатор-->
<script language="JavaScript" src="js/jquery.json-2.2.min.js"></script> <!-- $.toJSON -->
<title>Пример</title>
<style type="text/css">
body { font: 11pt Arial, Helvetica, sans-serif; margin: 0; }
h1 { font-size: 36px; margin: 0; color: #fc6; }
h2 { margin-top: 0; }
#header { padding: 10px; }
#sidebar { float: left; border: 1px solid #eee; width: 20%; padding: 5px; margin: 10px 10px 20px 5px; }
#sidebar p { margin-left:20px; }
#content { margin: 10px 5px 20px 25%; padding: 5px; border: 1px solid #eee; }
#footer { background: #eee; padding: 5px; color: #fff; clear: left; }
.error { padding:10px; width:80%;background-color:#ff4f4f; }
</style>
</head>
<body>
<div id="header"><h1>Какой - нибудь хеадер.</h1></div>
<div id="sidebar">
<h2>Ссылки</h2>
<p><a href="#">Главная</a></p>
<p><a href="#page/about">О сайте</a></p>
<p><a href="#page/contacts">Контакты</a></p>
<p><a href="#page/somepage">Какая - то страница</a></p>
<p><a href="#user/login">Авторизация</a></p>
</div>
<div id="content">
<h2>Какой - то контент </h2>
<div id="content_wrap"></div><!-- Именно сюда будут грузиться данные -->
</div>
<div id="footer">© 1</div>
</body>
</html>
Отметим наличие div с id= «content_wrap» — именно в него будут подгружаться загруженные шаблоны.
Далее напишем начальную загрузку контента.
var user = {};
/**
* Возвращает текущий hash без # в начале
**/
function hash() {
return document.location.hash.replace('#','');
}
function load(u) {
console.log('loading page '+ u +':');
$.ajax({
url: "/" + u,//я обращаюсь на страницу, которую мне нужно загрузить,
//т.к. на серверной стороне - все запросы идут к index.php,
//и там я могу найти тот контроллер, который мне нужен
type: "GET",
data: "action=pageLoad&url="+u+"", // action=pageLoad на серверной стороне - говорит мне, что нужно возвращать JSON
success: function(msg){//в этот момент, мы можем вернуть какие - то еще данные о юзере, из сервера, на текущий момент времени
var arr = $.parseJSON(msg);
console.log('success');
if (arr.my) {//в моем случае, я вернул в arr.my данные о пользователе
//обновляем данные о юзере
user = {
a: (arr.my.id)?true:false,
id:arr.my.id,
login: arr.my.login,
site_login: arr.my.site_login,
status: arr.my.status
}
}
document.location.hash = u;
render(arr, u);
console.log("load template");
}
});
}
function render(data, url) { //Грузит шаблон, параметр data - объект данных, передаваемых в шаблон; url - вида main/page , user/login
$("#content_wrap").show().html('');// обнуляем тот самый участок html-кода, в котором будет подгружена новая страница
$.get("jstemp/"+ url +".html", function(template) {//получаем html файл - шаблона
console.log("Template finded at jstemp/"+ url +".html");
$.tmpl(template, {
Content: data// в шаблоне tmpl все, переданные мною переменные будут хранится в массиве Content
}).appendTo('#content_wrap');//добавляем к #content_wrap
$("#content_wrap").prepend("<script language='JavaScript' src='js/included.js' />");//позже поясню для чего нужен этот код.
});
}
$(function(){//функция, вызываемая после загрузки DOM
if (hash()) {//если есть хэш
var url = hash();// он и будет ссылкой, но без # в начале.
} else {
var url = 'main/page';// ставим какое - нибудь дефолтное значение
}
load(url);//загружаем страницу.
});
На данном этапе, если все нормально, то можно загрузить главную страницу. Для примера напишу совсем малюсенький серверный код:
<?php
if(isset($_GET['action'])&&$_GET['action']=='pageLoad'){
switch($_GET['url']) {
case 'page/contacts':
$arr = array('name'=>'Контакты', 'info'=>'Связаться с нами можно с помощью сарафанного радио, ответ последует таким же образом.');
break;
case 'page/about':
$arr = array('name'=>'О сайте', 'info'=>'Этот сайт написан не профессионалами за еду.');
break;
case 'page/somepage':
$arr = array('name'=>'Еще одна страница', 'info'=>'Тут тоже что - то есть');
break;
case 'main/page':
default:
$arr = array('name'=>'Главная страница', 'info'=>'Разного рода информация');
break;
}
echo json_encode($arr);
} else {
?>
Тут Html код, который описан выше.
<?php } ?>
Теперь, при обращении к main/page — должно вывестись
Главная страница
Разного рода информация
Ура! 3/4 сделано. Теперь напишем простой скрипт, для отлова клики на ссылки.
/**
* Клик на ссылке
*/
$("a[href^='#']").unbind('click');//старое событие нужно убрать, иначе появляется много одинаковых событий на один клик.
$("a[href^='#']").click(function(e){//селектор - только ссылки, у которых параметр href начинается с символа #
var url = $("a:hover").attr('href').replace("#","");
if (page_url != url) {
if (url)
load(url);
else
load('main/page');
return false;
}
});
и поместим этот код в тот самый included.js
Назначение этого файла простое: При подгрузки html текста — скрипты, которые уже были на страницы, не хотят с ним работать, для этого приходит на помощь included.js.
А как же работа с формами, спросите вы? Да легко!
Работа с формами
Для примера создадим шаблон user/login.html
и поместим в него код:
{{if Content.error}}<!-- например сообщение о том, что пользователь уже залогинен -->
<div class="error login">${Content.error}</div>
{{else}}
<form class="form" id="login" action="" method="POST">
<div class="error" style="display:none;" id="message_login"></div><!-- Параметр id должен быть равен - message_[id_формы_выше] -->
<table>
<tr>
<td>Логин</td>
<td><input name="username[login]" type="text" value="" /></td>
</tr>
<tr>
<td>Пароль</td>
<td><input type="password" name="username[password]" value="" /></td>
</tr>
<br />
<tr>
<td colspan="2"><input value="Войти" style="width: 300px; height: 70px;" type="submit" /></td>
</tr>
</table>
</form>
{{/if}}
А в тот же included.js следует добавить:
/**
* сабмит формы
*/
$('form').submit(function() {//при отправке формы
var form = $(this);
var method = form.attr('method'); //забираем method формы
var id = form.attr('id'); //и id формы
$.ajax({ //url: не нужен, т.е. мы отправляем запрос на текущую страницу.
type: method,
data: "ajax=1&"+form.serialize(), //отправляем серверу все данные формы в виде строки + параметр ajax - что должно указать, что возвращать нужно JSON
success: function(msg){
var arr = $.parseJSON(msg);
if (arr.message) $("#message_"+id).show().html(arr.message);//если есть message(например ошибка), тогда вернем его в тот скрытый див.
if (arr.go) { // если есть go - то должен быть совершен переход на другую страницу, в нашем случае - без перезагрузок страницы.
if (arr.go == '' || arr.go == '/') arr.go = 'main/page';
page.load(arr.go);//грузим другую страницу
}
}
});
return false;
});
Обновим серверный код, чтобы было понятно на примере, что происходит:
<?php
error_reporting(0);
session_start();
if(isset($_POST['username'])){//если отправили форму авторизации
if(!$_SESSION['user']){//если еще не авторизированы
$user = $_POST['username'];
if($user['login']!='test'){ //если логин юзера не test, выдаем message
echo json_encode(array('message'=>'Пользователя '.$user['login'].' не существует'));
} else {//в противном случае перенаправляем юзера на главную страницу
$_SESSION['user'] = $user['login']; //просто для примера.
echo json_encode(array('go'=>'/'));
}
} else {//выдаем error если пользователь уже авторизирован
echo json_encode(array('error'=>'Вы уже авторизированы.'));
}
} else if(isset($_GET['action'])&&$_GET['action']=='pageLoad'){
switch($_GET['url']) {
case 'page/contacts':
$arr = array('name'=>'Контакты', 'info'=>'Связаться с нами можно с помощью сарафанного радио, ответ последует таким же образом.');
break;
case 'page/about':
$arr = array('name'=>'О сайте', 'info'=>'Этот сайт написан не профессионалами за еду.');
break;
case 'page/somepage':
$arr = array('name'=>'Еще одна страница', 'info'=>'Тут тоже что - то есть');
break;
case 'main/page':
default:
$arr = array('name'=>'Главная страница', 'info'=>'Разного рода информация');
break;
}
echo json_encode($arr);
} else {
?>
тут полный html код
<? } ?>
При POST запросе со страницы, мы проверяем авторизирован ли юзер, и если да — выводим ошибку, и убираем форму, в противном случае — проверяем логин пользователя, если не существует — выводим Message, иначе — отправляем юзера на главную страницу.
В заключении
В заключении хочу сказать, что данный способ не самый правильный, и тем более идеальный, но он заслуживает место быть. Надеюсь, тема статьи была интересна, и может быть, даже, кому — то окажется полезной сама статья.
Спасибо за внимание.
Update: ссылка на сорцы