СоХабр закрыт.

С 13.05.2019 изменения постов больше не отслеживаются, и новые посты не сохраняются.

| сохранено

H Пишем блог с Full-Fjax навигацией сами и с нуля. Часть 1 в черновиках Из песочницы

Всем привет.

В этой теме мы будем рассматривать реализацию full-ajax навигации на примере обычного блога. Если кому-то интересно, прошу под кат.

В очередной раз просматривая хабрахабр, меня посетила мысль наваять что-нибудь свое. Поэтому заказав пиццу и вооружившись большой кружкой чая — начал думать.
Я заметил, что на хабре много постов о том, как написать блог с использованием Fat-Free Framework, symfony, Zend и так далее… И тут у меня в голове закралась идея написать блог с ajax навигацией. А почему бы и нет? На хабре я не встретил такой статьи, может быть плохо искал…
Для того, чтобы пост не получился слишком большой я решил разделить его на несколько частей. Хочу обратить Ваше внимание на то, что это мой первый пост на хабре, да и писарь из меня никудышный. Поэтому, если что не так, извиняйте.

Итак, приступим.

Демо.

Ссылки в нашем блоге будут следующего вида:

http://localhost/#!/
http://localhost/#!/page
http://localhost/#!/another-apge

Такие ссылки Вы могли видеть в твиттере.

В нашей корневой директории создаем индексный файл, в котором будет немного верстки нашего кульного блога.
Я над дизайном заморачиваться не стал, так как это пост не об этом. Поэтому без комментариев выкладываю его содержание:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
    <meta name="description" content="" />
    <meta name="keywords" content="" />
    <meta name="generator" content="" />
    <title>My thirst Ajax Blog</title>
    <script type="text/javascript" src="inc/ajax.js"></script>
    <script src="//ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script>
    <link type="text/css" rel="stylesheet" href="style.css"/>
</head>
<body>
    <div id="content">
        <h1><a href="/#!/">Мой блог</a> <span>v1.0</span></h1>
        <p>
            <a href="/#!/">Home page</a>
            <a href="/#!/about">About page</a>
        </p>
        <hr />
        <div id="posts"></div>
    </div>
    <div id="preloader">
        <div style="position: absolute; left: 50%; margin-left: -50px; top: 50%; margin-top: -50px;">
                <img src="loading.gif" title="Loading..." alt="Loading.." />
        </div>
    </div>
</body>
<script>
    $(document).ready(function(){
        ajax.onLoadStart = function(){
            $('#preloader').show();
        };
        ajax.onLoadEnd = function(){
            $('#preloader').hide();
        };
        ajax.init();
    });
</script>
</html>



Единственное, на что хотелось бы обратить внимание, это яваскрипт в конце страницы, который собственно и будет инициализировать нашу ajax-навигацию по блогу.

Чуть не забыл про стили, содержимое style.css:

html, body, div, ul {
	margin: 0;
	padding: 0;
}

body {
    color: #262626;
	background: #f4f4f4;
	font: normal 12px/18px Verdana, sans-serif;
}

#content {
	position: absolute;
	width: 800px;
	right: 0px;
	left: 0px;
	margin: 40px auto 0 auto;
	padding: 0 60px 30px 60px;
	border: solid 1px #cbcbcb;
	background: #fafafa;
	-moz-box-shadow: 0px 0px 10px #cbcbcb;
	-webkit-box-shadow: 0px 0px 10px #cbcbcb;
}

h1 {
	margin: 30px 0 15px 0;
	font-size: 30px;
	font-weight: bold;
	font-family: Arial;
}

h1 span {
	font-size: 50%;
	letter-spacing: -0.05em;
}

hr {
	border: none;
	height: 1px; line-height: 1px;
	background: #E5E5E5;
	margin-bottom: 20px;
	padding: 0;
}

p {
	margin: 0;
	padding: 7px 0;
}

a {
	outline: none;
}

link {
	color: #000;
	text-decoration: none;
}
a:visited, a:link {
	color: #000;
	text-decoration: none;
}
a:hover {
	color: #000;
	text-decoration: none;
}
a:active {
	color: #000;
	text-decoration:none;
}

#preloader{
    display: none;
    position:fixed;
    z-index: 777;
    background:none repeat scroll 0 0 #000000;
    cursor:wait;
    width:100%;
    height:100%;
    top:0;
    left:0;
    opacity:0.7;
    -moz-opacity: 0.5;
    -khtml-opacity: 0.5;
}


Здесь все понятно, идем дальше… Файл ajax.js:

var ajax = {

    /**
     * Скрипт, который будет обрабатывать наши ajax запросы.
     */
    ajSrc: 'ajax.php',
    ajUrl: '',
    /**
     * Функция, которая будет вызываться при начале загрузки
     */
    onLoadStart: function(){},
    /**
     * Функция, которая будет вызываться после получения ответа
     */
    onLoadEnd: function(){},

    /**
     * Мы организовали навигацию при помощи урлов вида #!/my-url.
     * Поэтому эта функция проверяет адресную строку на соответствие урла такому виду и в случае чего, отправляет запрос.
     * @return {Boolean}
     */
    checkAjaxNav: function(){
        hash    = document.location.hash;
        navInd  = hash.substr(0, 3);

        if(navInd != '#!/'){
            this.ajUrl = hash;
            return false;
        }

        hash = hash.substr(2);
        if( hash != this.ajUrl ){
            this.ajUrl = hash;
            this.sendData(hash);
        }
        return true;
    },

    /**
     * Отправляем запрос на сервер
     * @param ajaxData
     */
    sendData: function(ajaxData){
        this.onLoadStart();
        $.post(this.ajSrc, {
            ajax_data: ajaxData
        }, function( data ){
            ajax.receiveData(data);
        }, 'json');
    },
    /**
     * Получаем ответ от сервера и обрабатываем его.
     * @param data
     */
    receiveData: function( data ){
        for( var key in data ){
            var val = data[key];
            if( 'eval' == key )
                eval(val);
            else
                $(key).html(val);
        }

        this.onLoadEnd();
    },

    /**
     * Инициализируем нашу навигацию
     */
    init: function(){
        this.checkAjaxNav();
        $(window).bind('hashchange', function(){
            ajax.checkAjaxNav();
        });
    }
};



Здесь видно, что мы будем отправлять запросы функцией sendData() на наш обработчик ajax.php.
Перед тем, как запрос будет отправлен, вызывается функция onLoadStart() в которой можно выполнять что-то. Например показать пользователю красивый индикатор загрузки, прикрутить валидацию форм и т.д.

После того, как наш запрос будет обработан, вызывается функция receiveData(), в которой мы обрабатываем полученные данные и вызываем функцию onLoadEnd(), которая скрывает наш индикатор загрузки и выполняет какие-то другие функции. Например, ре-инициализация форм страницы и т.д

Обработчик ajax.php

<?php
error_reporting(0);

define("coolBlogCms", true);
define("DIR", getcwd() . "/");
define("INC", DIR . "inc/");

require_once INC . "myAjax.php";

$blog = new myAjax();

if( isset($_POST['ajax_data']) ){
    $ew = $blog->receiveData();

    switch($ew[1]){
        case 'about':
            $html['#posts'] = <<<HTML
    This is about page. Loaded from my ajax cms.
HTML;
            break;
        default:
            $html['#posts'] = <<<HTML
    This is the main page of my cool blog. Thank's for reading.
HTML;

            break;
    }

    $blog->sendData($html);
}

?>



Это небольшой контролер, который будет обрабатывать наши запросы и данные. Пока что здесь нет ничего сверх естественного.
Функция receiveData() получает данные POST-запроса. А sendData() отправляет данные. Сейчас это только json, но дальше мы прикрутим еще что-нибудь.
Данные мы будем отправлять массивом, в котором ключом будет выступать селектор элементов или #id элемента на конечной странице, а значением — некие данные.
Так же этой функцией мы можем отправлять js код, который затем выполнится на странице при помощи eval()
Пример для наглядности:

$arr = array();
    $arr['#first_div'] = "First div content";
    $arr['#second_div'] = "Second div content";
    $arr['.another_divs'] = "Content that will be placed to several divs";

    $blog->sendData($arr, "alert('Hello world !');");


myAjax.php

<?php

if(!defined("coolBlogCms"))
    exit;

class myAjax{

    /**
     * @return bool
     */
    public function receiveData(){
        if( !isset($_POST['ajax_data']) ){
            return false;
        }

        $data = $_POST['ajax_data'];
        $data = explode('/', $data);
        unset( $_POST['ajax_data'] );

        return $data;
    }

    /**
     * @param $data
     * @param string $eval
     * @return bool
     */
    public function sendData( $data, $eval = '' ){
        if(!$data && !$eval)
            return false;

        if( !headers_sent() ){
            header('Content-Type: application/json; charset=utf-8' );
        }

        echo json_encode( array_merge($data, array('eval' => $eval)) );
        return true;
    }
}



Вот и все. Если читателям будет интересно продолжение, то я с радостью напишу вторую часть статьи. Примерное содержание второй части:
  • Организация БД для хранения постов, комментариев
  • Обработчик форм
  • * Допиливание существующего функционала
  • Оптимизация кода


В качестве БД можно расмотреть такие варианты как традиционная MySQL, или MongoDb.
* Планируется добавление xml и plain типов данных.

Если у Вас есть какие-либо пожелания, о том, что бы Вы хотели видеть в следующей статье — пишите.
Если у Вас есть свое мнение, по поводу того, что и как можно было бы сделать лучше — пишите, буду рад.

Так же рад буду и конструктивной критике.

комментарии (14)

+1
mcavalon ,  
Если читателям будет интересно продолжение, то я с радостью напишу вторую часть статьи

наверно не будет…
+5
artishok ,   * (был изменён)

Пора забыть про #! и переходить на History API. Да и моргающий прелоадер раздражает.

0
byabuzyak ,  

Спасибо.
Про history API знаю, но в статье почему-то решил использовать #!/

0
KenAdams ,  

Как обстоят дела у Histoty API в плане SEO? слышал, что Google, как раз индексирует страницы с URL #!

0
artishok ,  

У меня ссылки загружаемые через аякс и обычные ссылки имеют одинаковый вид. При генерации html я проверяю запрашивается страница аяксом или нет. Если да, то я отдаю только контент без шапки, футера, и прочего ненужного. Если нет — html генерируется как обычно. SEO не страдает в таком случае.

0
KenAdams ,  

Не много не понял, проблема с ajax загрузкой в том, что когда робот заходит на сайт, он получает то что он получает, а ajax уже после этого подгружает в DOM нужный контент, из-за чего роботу не удается проиндексировать асинхронный контент, следовательно роботу надо отдавать другой вариант страницы, без ajax?

0
rOOse ,  

Зачем, просто при заходе по полной ссылке отдается полная версия страницы, а при аякс запросе отдается только контент.

0
byabuzyak ,   * (был изменён)

Для приведенного случая, когда для навигации используется #!/ у гугла есть стандарт Google AJAX Crawling который позволяет заставить его запрашивать необходимый адрес. Но это менее удобно конечно же, чем использование варианта с обычными линками.

0
mr_avi ,   * (был изменён)

Мутно очень, можно проще. Да и History.js можно использовать для изменения ссылок через JS в адресной строке в новых браузерах, и через хеш в старых. + все должно работать без JS для поисковиков.

0
byabuzyak ,  

С радостью посмотрел бы на Ваш вариант по проще.

+ все должно работать без JS для поисковиков.


Это я думал вынести в следующую часть. Которой как видимо не будет
0
mr_avi ,   * (был изменён)

Не совсем то, но вот мой музыкальный плеер на Silex-е + Twig, все работает на ajax-е, все ссылки работают и для поисковиков.
github.com/uavn/oplayer3
Хотя и здесь не оптимально, ну и специфика другая.

Для History.js см. файл github.com/uavn/oplayer3/blob/master/web/js/main.js

+1
mr_avi ,  

> Которой как видимо не будет
Пусть будет, просто учтите информацию из комментариев.

0
byabuzyak ,  

Спасибо, обязательно учту.

0
dshster ,  

Сколько лет эта статья лежала в черновиках? Технологии уже убежали далеко вперёд и даже bind в jquery уже давным давно не используется. Хотя у вас и jquery 1.10… Неужели занимаясь веб-разработкой можно совсем не интересоваться, что происходит в этой сфере.

Ощущение, что автор написал статью лет 8 назад, потом забыл про неё, а сейчас вспомнил и опубликовал как было. Здесь даже критиковать нечего. Я даже ajax термин уже много времени не встречаю, технология стала настолько обыденной, что уделять отдельное внимание ей уже нет смысла.