Динамическое обновление и смена контента при переходе по URL без перезагрузки страницы сайта HTML5, JavaScript, PHP, jQuery
4 года назад

Динамическое обновление и смена контента при переходе по URL без перезагрузки страницы сайта HTML5, JavaScript, PHP, jQuery

Современные тенденции и подходы в веб-разработке открывают новые возможности использования динамики переходов по URL – без перезагрузки страниц. Данный способ, заключается в создании алгоритма, при котором пользователь, просматривая сайт не обновляет страницу целиком, а подгружает лишь некоторые её части и блоки, что значительно увеличивает скорость загрузки сайта и зрительно способствует лучшему восприятию.

В свою бытность подобные задачи, реализовывались посредством использования фреймов, однако пользователи переходя по ссылкам, перемещались по страницам скачкообразно, нарушая видимое место перехода, которое надо было снова искать. Зрительно это выглядело не совсем красиво, да и URL адрес основного фрейма никак не менялся, оставался прежним. Скопировать такую ссылку было нельзя.

В данной статье мы поговорим о том, как используя HTML5, JavaScript, PHP и jQuery реализовать динамическую подгрузку контента, со сменой URL, при этом не перезагружая страницу сайта целиком.

А теперь давайте приступим! Создадим и откроем файл index.php и пропишем туда нужную нам разметку:
cont.php
<?php
    include("class.php");
?>
<!doctype html>
<html lang="ru">
<head>

<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<meta http-equiv="x-ua-compatible" content="ie=edge">
<meta name=viewport content="width=device-width, initial-scale=1"> 

<link rel="stylesheet" href="style.css" type="text/css" media="screen">

<script src="jquery-3.2.1.min.js"></script>
<script src="history.js"></script>
<script src="document.js"></script>

<title><?php echo $data->title(); ?></title>
</head>
<body>

<div id="footer-l">
    <div id="page-l">
    <div id="logo">logo</div>
    <nav><ul class="nav"><?php echo $data->menu(); ?></ul></nav>
    </div>
</div>

<div id="footer-r">
    <div class="content"><?php echo($data->content($data->state(),$data->title())); ?></div>
</div>

</body>
</html>
В представленном коде как мы видим ничего сложного нет, в файле index.php содержится в основном html-разметка страницы.

$data->title() – вывоз функции названия страницы

$data->menu() – меню сайта

$data->content() – вывод основного содержимого блока content

А теперь создадим и откроем файл class.php и пропишем:
class.php
<?php

$menu = array(
    '1' => array("name" => "Главная", "page" => "./"),
    '2' => array("name" => "О нас", "page" => "./about"),
    '3' => array("name" => "Контакты", "page" => "./contacts")
);

class load_page_vars {

    function title(){

        global $menu;
        $item = $menu;

        $str = '';
        $i=0;
        foreach($item as $k => $value)
        {
            $i++;
            if($item[$i]["page"] == ".".$this->state())
            {
                return $item[$i]["name"];
            }        
        }
    }

    function menu(){

        global $menu;

        $str = '';
        $i=0;
        foreach($menu as $k => $value)
        {
            $i++;
            $str.= "<li><a href='".$menu[$i]["page"]."' ".($menu[$i]["page"] == ".".$this->state() ? 'class="selected"' : '')." title='".$menu[$i]["name"]."'>".$menu[$i]["name"]."</a></li>";
        }
        //$this->state($page_title);
        return $str;
    }



    function content($page, $name){

        $post = array(
            'page' => ".".$page,
            'name' => $name,
            'password' => 'bar',
            'submit' => TRUE,
        );
         
        $data = http_build_query($post);

        $opts = array(
                  'http' => array(
                      'method' => 'POST',
                      'header' => "Content-type: application/x-www-form-urlencoded\r\nContent-Length: " . strlen($data) . "\r\n",
                      'content' => $data,
                  )
               );

        $context  = stream_context_create($opts);

        $url = $this->siteURL()."/content.php";
        $content = file_get_contents($url,FALSE,$context);

        return $content;
    }

    function state() {
        $request = substr($_SERVER['PHP_SELF'], 0, strrpos($_SERVER['PHP_SELF'], '/'));
        $str_repl = str_replace($request, '', $_SERVER['REQUEST_URI']);
        return $str_repl;
    }

    function siteURL()
    {
        if (isset($_SERVER['HTTPS']) &&
            ($_SERVER['HTTPS'] == 'on' || $_SERVER['HTTPS'] == 1) ||
            isset($_SERVER['HTTP_X_FORWARDED_PROTO']) &&
            $_SERVER['HTTP_X_FORWARDED_PROTO'] == 'https') {
          $protocol = 'https://';
        }
        else {
          $protocol = 'http://';
        }

        $siteUrl = $protocol.$_SERVER["HTTP_HOST"].$_SERVER["PHP_SELF"];

        return dirname($siteUrl);

    }

}

$data = new load_page_vars();



?>
В class.php содержатся функции, отвечающие за вывод названия страницы, меню, контент
document.js
window.onload = function() {

    function reAnswer(state, title){
        $.ajax ({
            url: "content.php",
            type: "POST",
            data: {page: state, name:title},
            success: function (result) { $(".content").html(result);
             }
        });
    }

    function handlerAnchors() {
        $("ul.nav li a").removeClass('selected');
        $(this).addClass('selected');

        var state = {
            title: this.getAttribute( "title" ),
            url: this.getAttribute( "href", 2 )
        }

        history.pushState( state, state.title, state.url );
        document.title = state.title;
        reAnswer(state.url, state.title);

        return false;
    }

    var anchors = document.getElementsByTagName( 'a' );
    for( var i = 0; i < anchors.length; i++ ) {
        anchors[ i ].onclick = handlerAnchors;
    }

     window.onpopstate = function( e ) {
         $("ul.nav li a").removeClass('selected');
         $('ul.nav li a[href$="' + history.state.url + '"]').addClass('selected');
         document.title = history.state.title;
         reAnswer(history.state.url, history.state.title);
    }
}
Функция reAnswer отправляет файлу content.php POST массив с данными нужной нам страницы. При успешной отправки и получении данных, функция success, выводит в блок .content описание страницы, согласно введенному URL адресу.

Функция handlerAnchors вызывается при клике и смене адреса страницы. Она содержит массив state, в который записываются адреса и названия посещенных страниц.

history.pushState записывает в историю браузера адреса страниц, которые позволяют пользователям использовать браузерную кнопку назад.

Для наиболее лучшего восприятия, добавим немного стилизации.
style.css
@import url('https://fonts.googleapis.com/css?family=Open+Sans|Roboto');

html, body {
    height: 100%;
}

body {
    color: #4f4f5a;
    font-family: 'Roboto', sans-serif;
    font-size: 16px;
    padding: 0;
    margin: 0;
}

.nav {
    margin-top: 50px;
    padding: 0;
}
.nav:after {
    content: "."; 
    display: block;
    height: 0; 
    clear: both; 
    visibility: hidden;
}

.nav li {
    margin: 0 10px 0 0;
    padding: 0;
    list-style: none;
}

.nav li a {
    padding: 10px 15px;
    margin: 3px 0;
    display: table;
    color: #000;
    font-size: 18px;
    text-decoration: none;
}

.nav a:hover {
    background: #f6f6f6;
    -webkit-border-radius: 2px;
    -moz-border-radius: 2px;
    border-radius: 2px;  
}

.nav a:focus {
    outline: none;
}

.nav .selected {
    background: #f6f6f6;
    color: #000;
    text-decoration: none;
    -webkit-border-radius: 2px;
    -moz-border-radius: 2px;
    border-radius: 2px;
}

.content {
    width: calc(100% - 180px);
    margin: 70px 70px 70px auto;
    padding: 20px;
    height: 300px;
    overflow: auto;
    border: 1px solid #f3f3f3;
    background: #FFF;
    color: #000;
}

.content p {
    margin: 0 0 10px;
}

#page-l {
    padding: 30px;
}

#logo {
    display: table;
    margin: 20px 0;
    background: #f66035;
    color: #fff;
    font-weight: bold;
    padding: 5px;
    font-size: 26px;
    -webkit-border-radius: 2px;
    -moz-border-radius: 2px;
    border-radius: 2px;
}

#footer-l {
    float: left;
    width: 300px;
    height: 100%;
    background: #fff;
    -webkit-box-shadow: -6px 0px 15px 0px rgba(0,0,0,0.75);
    -moz-box-shadow: -6px 0px 15px 0px rgba(0,0,0,0.75);
    box-shadow: -6px 0px 15px 0px rgba(0,0,0,0.75);
}

#footer-r {
    float: left;
    width: calc(100% - 302px);

}

#footer-r:after {
    display : table;
    content : " ";
    clear : both;
}

@media (max-width:550px) {
#footer-l, #footer-r {
    width: 100%;
}
#footer-l {
    #border: 0 none;
    height: auto;
}
.content {
    width: calc(100% - 40px);
    margin: 0;
    border: 0 none;
}
}
content.php
<?php

switch ($_POST["page"]) {
    case "./":
        echo "<h1>".$_POST["name"]."</h1>";
        echo "<p>Текст...</p>";
    break;

    case "./about":
        echo "<h1>".$_POST["name"]."</h1>";
        echo "<p>Текст...</p>";
    break;

    case "./contacts":
        echo "<h1>".$_POST["name"]."</h1>";
        echo "<p>Текст...</p>";
    break;
}

?>
В файл content.php посредством POST запроса отправляется массив, согласно которому switch выводит нужную нам информацию на страницу.
.htaccess

RewriteEngine on

RewriteCond %{THE_REQUEST} ^[A-Z]{3,9}\ /(.*)index\.php($|\ |\?)
RewriteRule ^ /%1 [R=301,L]

# Sets the base folder of the State sample
RewriteBase  /webdevelopment/dynamic-page

RewriteCond  %{REQUEST_FILENAME}    !-f
RewriteCond  %{REQUEST_FILENAME}    !-d
RewriteRule  .* index.php

SetEnvIf X-Forwarded-Proto https SERVER_PORT=443
SetEnvIf X-Forwarded-Proto https HTTPS=on
RewriteCond %{HTTP:HTTPS} !=on [NC]
RewriteRule ^(.*)$ https://%{SERVER_NAME}%{REQUEST_URI} [R=301,L]
17,5K
Комментарии
  • Arlo 3 года назад
    Thank you very much, this is the best version of the code.
    Ответить
  • Сергей3 года назад
    Добрый день. Классный код, очень долго искал такое решение. Спасибо большое Вам! Вопрос history.js почему не приложили код и вообще он нужен? Зачем?
    Ответить
  • No name3 года назад
    history.js нужен для реализации переходов без перезагрузки страницы
    Ответить
  • Ник3 года назад
    Файл history.js задаёт историю браузеру, чтобы при нажатии кнопки назад контент подгружался также динамично как и при переходах.
    Ответить
  • Sovlarus3 года назад
    Все сделал ровно по инструкции, но при нажатии на пункт меню меня выкидывает на несуществующую страницу. Например, О нас ведет на страницу about, а замены контента не происходит.
    Ответить
    • Сергей3 года назад
      Sovlarus, скорее всего у вас конфликт правил в файле htaccess. Модуль modrewrite.c включен?
      Ответить
  • Ильдар3 года назад
    Все ровно при клике в подгружаемом контенте страница обновляется как ни крути
    Ответить
    • Simple3 года назад
      Ильдар, history.js подключен?
      Ответить
  • Дмитрий3 года назад
    При попытке обновить страницу, ошибка 500. Internal Server Error. Любая страница, кроме главной. Почему так происходит ?
    Ответить
  • Александр3 года назад
    Все круто! Спасибо! Только еще вопрос как создать ссылку на конкретную страницу? не из массива. Например в тексте страницы помимо меню будет еще ссылка на другую страницу
    Ответить
  • Дмитрий3 года назад
    Где файл history.js????? Нечего не работает зачем такое выкладывать отстой твой сайт г....о!!!!
    Ответить
  • SERG2 года назад
    Не гоните пургу! Все работает! Если нужен history.js, открой исходный код демо и скачай оттуда, тормоз!!!
    Ответить
  • Алекс2 года назад
    Все работает, кто не разобрался совсем тук-тук!
    Ответить
  • Алексей2 года назад
    Может я что-то не понял, но твой демо не работает без перезагрузки... каждый раз вижу обновление
    Ответить
  • irmaseoru2 года назад
    Спасибо, очень полезная информация
    Ответить
  • Александр2 года назад
    Очень круто! Огромное спасибо
    Ответить
  • Farmer1 год назад
    Спасибо за годный пример! Для тех, кому нужен history.js - как писали выше, открывайте демо, нажимайте f12, ищите подключаемый файл history.js, открывайте его в новой вкладке и копируйте содержимое. Для тех, у кого при обновлении страницы вылетает ошибка - в файле .htaccess в строке RewriteBase поменяйте путь до своего index файла, или поставьте /
    Ответить
  • Евген1 год назад
    Хватит уже говнокодить.
    Ответить
  • Александр4 месяца назад
    Очень крутое решение, огромнейшее спасибо. На основе данного кода хочу создать базовый движок для сайта.
    Ответить