Нередко в коде страницы требуется получить с сервера некоторый код HTML. Например, страница может представлять одностраничный сайт, который через ajax запрос загружает необходимый html-код и вставляет на страницу. Поэтому рассмотрим, как через AJAX загрузить код html.
В качестве сервера, как и в прошлой статье, будем использовать Node.js как наиболее простой вариант, но естественно при желании можно использовать любую другую технологию серверного уровня или какой-нибудь веб-сервер.
Итак, определим для проекта на жестком диске папку, в которой создадим три файла:
index.html: главная страница приложения
home.html: страница с кодом html, который мы будем загружать через AJAX
server.js: файл приложения сервера, который будет использовать Node.js
Файл server.js будет представлять код сервера Node.js. Определим в нем следующий код:
const http = require("http"); const fs = require("fs"); http.createServer((request, response)=>{ // получаем путь после слеша, слеш - первый символ в пути let filePath = request.url.substring(1); // если пустой путь, отправляем главную страницу index.html if(!filePath) filePath = "index.html"; // в качестве типа ответа устанавливаем html response.setHeader("Content-Type", "text/html; charset=utf-8;"); fs.readFile(filePath, (error, data)=>{ if(error){ // если ошибка response.statusCode = 404; response.end("<h1>Resourse not found!</h1>"); } else{ response.end(data); } }); }).listen(3000, ()=>console.log("Сервер запущен по адресу http://localhost:3000"));
Вкратце пробежимся по коду. Сначала подключаются пакеты с функциональностью, которую мы собираемся использовать:
const http = require("http"); // для обработки входящих запросов const fs = require("fs"); // для чтения файлов с жесткого диска
Для создания сервера применяется функция http.createServer(). В эту функцию передается функция-обработчик, которая вызывается каждый раз, когда к серверу приходит запрос.
Эта функция имеет два параметра: request
(содержит данные запроса) и response
(управляет отправкой ответа).
В функции-обработчике с помощью свойства request.url
мы можем получить путь к ресурсу, к которому пришел запрос. Нам надо обрабатывать запросы к
страницам "index.html" и "home.html" (а в перспективе к любым другим страницам html). Путь всегда начинается со слеша "/". Например, запрос к странице "home.html" будет
представлять путь "/home.html". Соответственно, чтобы получить из запрошенного пути путь к файлам на жестком диске, нам надо убрать начальный слеш:
let filePath = request.url.substring(1);
Однако если запрос обращен к корню сайта, то путь состоит только из одного слеша - "/". Соответственно, если мы удалим этот слеш, то получим пустую строку. Поэтому если запрос идет к корню веб-приложения, то будем считать что запрос идет к главной странице - index.html:
if(!filePath) filePath = "index.html";
И поскольку в нашем случае ответ сервера будет представлять код html, то с помощью метода setHeader()
устанавливаем для
заголовка "Content-Type" значение "text/html":
response.setHeader("Content-Type", "text/html; charset=utf-8;");
То есть ответ сервера будет представлять html.
Далее с помощью функции fs.readFile считываем файл, к которому идет запрос. Первый параметр функции - адрес файла (в данном случае предполагается, что файл находится в одной папке с файлом сервера server.js). Второй параметр - функция, которая вызывается после считывания файла и получет его содержимое через свой второй параметр data. Вполне возможно, что запрошенного файла не окажется, и в этом случае отправляем ошибку 404:
fs.readFile(filePath, (error, data)=>{ if(error){ // если ошибка response.statusCode = 404; response.end("<h1>Resourse not found!</h1>"); }
Если ошибки нет, файл найден и успещно считан, то отправляем параметр data, который содержит данные файла:
else{ response.end(data); }
В конце с помощью функции listen() запускаем веб-сервер на 3000 порту. То есть сервер будет запускаться по адресу http://localhost:3000/
Файл home.html будет содержать простенький код, который будет загружаться веб-страницей. Пусть это будет следующий код:
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>Home Page</title> </head> <body> <h1>Home Page</h1> <p>Home Page Text</p> </body> </html>
Теперь определим код главной страницы index.html, которая будет загружать страницу home.html
:
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>METANIT.COM</title> </head> <body> <script> const xhr = new XMLHttpRequest(); xhr.onload = () => { // обработчик получения ответа сервера if (xhr.status == 200) { // если код ответа 200 const html = xhr.responseText; // получаем ответ console.log(html); // выводим полученный ответ на консоль браузера } else { // иначе выводим текст статуса console.log("Server response: ", xhr.statusText); } }; xhr.open("GET", "/home.html"); // GET-запрос к ресурсу /home.html xhr.setRequestHeader("Accept", "text/html"); // принимаем только html xhr.send(); // выполняем запрос </script> </body> </html>
В обработчике загрузке xhr.onload
получаем текст ответа через xhr.responseText
и выводим ответ на консоль.
Теперь в консоли перейдем к папке сервера с помощью команды cd и запустим сервер с помощью команды node server.js
C:\app>node server.js Сервер запущен по адресу http://localhost:3000
После запуска сервера мы можем перейти в браузере по адресу http://localhost:3000, нам отобразится страница, в javascript-коде которой произойдет обращение к странице "home.html". Код javascript получит эту страницу и выведет ее содержимое на консоль:
В примере выше мы получали содержимое страницы как обычный текст. Однако так как этот текст фактически содержит разметку HTML, то мы можем загрузить его на веб-страницу. Так, изменим код страницы index.html следующим образом:
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>METANIT.COM</title> </head> <body> <div id="content"></div> <script> const contentDiv = document.getElementById("content"); // элемент для загрузки html const xhr = new XMLHttpRequest(); xhr.onload = () => { if (xhr.status == 200) { contentDiv.innerHTML = xhr.responseText; // выводим полученный ответ в contentDiv } else { // иначе выводим текст статуса console.log("Server response: ", xhr.statusText); } }; xhr.open("GET", "/home.html"); // GET-запрос к ресурсу /home.html xhr.setRequestHeader("Accept", "text/html"); // принимаем только html xhr.send(); // выполняем запрос </script> </body> </html>
В данном случае загружаем полученный код страницы "home.html" в элемент c id=content
Однако проблема в данном случае состоит в том, что код страницы "home.html" кроме собственно некоторого содержимого также содержит элементы head, title,
метаописания страницы с помощью тегов <meta>
. Эти элементы нет смысла загружать на другую веб-страницу. Либо мы хотим загрузить какой-то определенный элемент
со страницы "home.html", а не весь ее код. В этом случае мы можем получить ответ через свойство responseXML и затем манипулировать ответом как стандартным документом html. Например, изменим код javascript следующим образом:
const contentDiv = document.getElementById("content"); const xhr = new XMLHttpRequest(); xhr.onload = () => { // обработчик получения ответа сервера if (xhr.status == 200) { // загружаем только содержимое элемента body contentDiv.innerHTML = xhr.responseXML.body.innerHTML; } else { console.log("Server response: ", xhr.statusText); } }; xhr.open("GET", "/home.html"); // GET-запрос к ресурсу /home.html xhr.responseType = "document"; // устанавливаем тип ответа xhr.setRequestHeader("Accept", "text/html"); // принимаем только html xhr.send(); // выполняем запрос
Здесь следует отметить два момента. Прежде всего устанавливаем для ответа тип "document":
xhr.responseType = "document";
Это позволит нам получить ответ как объект типа Document, аналогичный тому, что представляет свойство document
на веб-странице.
Чтобы получить ответ в виде html/xml используем свойство responseXML. И далее, поскольку это свойство представляет объект Document,
используем свойство body
для обращения к непосредственному содержимому страницы:
contentDiv.innerHTML = xhr.responseXML.body.innerHTML;
В результате в contentDiv будет загружено содержимое элемента body страницы "home.html".
Подобным образом можно обращаться к другим свойствам объекта Document. Например, получим заголовок:
document.title = xhr.responseXML.title;
Или загрузим на страницу только текст из заголовка <ht1>
:
contentDiv.innerHTML = xhr.responseXML.querySelector("h1").textContent;
Возможность загружать html-код и вставлять его на страницу позволяет нам пойти дальше и разделить функционал приложения на несколько компонентов и при необходимости подгружать их. Например, пусть в проекте у нас есть следующие файлы:
server.js: файл приложения сервера на Node.js
index.html: главная страница приложения
home.html: файл компонента home
about.html: файл компонента about
contact.html: файл компонента contact
Файл приложения сервера на Node.js - server.js остается тем же, что был определен выше в данной статье.
Пусть файл home.html содержит какой-нибудь простейший код типа следующего:
<h1>Home Page</h1> <p>Home Page Text</p>
Файл about.html пусть выглядит аналогичным образом:
<h1>About Page</h1> <p>About Page Text</p>
И код файла contact.html:
<h1>Contact Page</h1> <p>Contact Page Text</p>
Эти файлы представляют компоненты, которые будут загружаться на главной странице.
На главной странице index.html определим следующий код:
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>METANIT.COM</title> </head> <body> <nav><a href="home">Home</a> | <a href="about">About</a> | <a href="contact">Contact</a></nav> <div id="content"></div> <script> const contentDiv = document.getElementById("content"); function loadContent(fileName){ const xhr = new XMLHttpRequest(); xhr.onload = () => { if (xhr.status == 200) { contentDiv.innerHTML = xhr.responseText; // xhr.responseXML.body.innerHTML; document.title = fileName; } }; xhr.open("GET", fileName + ".html"); // GET-запрос по адресу ссылки xhr.setRequestHeader("Accept", "text/html"); // принимаем только html xhr.send(); // выполняем запрос } // устанавливаем обработчик нажатия для кнопок const links = document.getElementsByTagName("a"); for (let i = 0; i < links.length; i++) { links[i].addEventListener("click", (e)=>{ loadContent(links[i].getAttribute("href")); e.preventDefault(); }); } // по умолчанию загружаем компонент home loadContent("home"); </script> </body> </html>
Здесь для навигации по компонентам на страницу помещаем ряд ссылок:
<nav><a href="home">Home</a> | <a href="about">About</a> | <a href="contact">Contact</a></nav>
Адрес каждой такой ссылки совпадает с названием страницы соответствующего компонента без расширения ".html".
Каждый из компонентов будет загружаться на странице в элемент с id="content", который получаем в коде JavaScript в константу contentDiv:
const contentDiv = document.getElementById("content");
Также в коде JavaScript для каждой ссылки устанавливаем обработчки, в котором вызываем функцию loadContent и в которую передаем значение атрибута href ссылки - то есть адрес компонента
const links = document.getElementsByTagName("a"); for (let i = 0; i < links.length; i++) { links[i].addEventListener("click", (e)=>{ loadContent(links[i].getAttribute("href")); e.preventDefault(); }); }
В функции loadContent используем адрес ссылки для отправки ajax-запроса, а ответ (полученный html) загружаем в элемент contentDiv
contentDiv.innerHTML = xhr.responseText; // xhr.responseXML.body.innerHTML;
При загрузке страницы сразу загружаем код компонента home, как компонента по умолчанию:
loadContent("home");
Таким образом, на главной странице мы сможем обращаться к конкретным компонентам, переходя по ссылкам: