Нередко в коде страницы требуется получить с сервера некоторый код 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");
Таким образом, на главной странице мы сможем обращаться к конкретным компонентам, переходя по ссылкам: