Настало время доделать наш текстовый редактор и добавить в него возможность работы с несколькими документами. Если вы не знаете, о чём идёт речь — прочитайте наши статьи о том, как сделать редактор с автосохранением текста, и как улучшить его внешний вид.
Алгоритм будет такой:
- добавляем слева список документов, чтобы можно было переключаться между ними;
- клик по названию документа делает его активным и переключает редактор на работу именно с этим документом;
- ALT + клик по названию в списке — удаляет документ;
- как и раньше, мы будем сохранять каждое нажатие клавиши в окне редактора.
Чтобы не тратить время и силы зря, используем для списка документов уже готовый код из статьи про список задач. Мы объединим эти две программы в одну — программисты часто пользуются этим подходом, чтобы быстро получить нужный результат.
<!DOCTYPE html>
<html>
<!-- служебная часть -->
<head>
<!-- заголовок страницы -->
<title>Текстовый редактор</title>
<!-- настраиваем служебную информацию для браузеров -->
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- задаём CSS-стили прямо здесь же, чтобы всё было в одном файле -->
<style type="text/css">
/*задаём общие параметры для всей страницы: шрифт и отступы*/
body {
text-align: left;
margin: 10;
font-family: Courier New, Courier;
font-size: 20px;
background-color: lightgray;
}
/* оформляем окно редактора */
.editorSheet {
width: 80vw;
min-height: 100vw;
margin-left: 10vw;
border: solid;
border-width: 0px;
text-align: left;
background-color: white;
-webkit-box-shadow: 6px 10px 9px 0px rgba(0, 0, 0, 0.75);
-moz-box-shadow: 6px 10px 9px 0px rgba(0, 0, 0, 0.75);
box-shadow: 6px 10px 9px 0px rgba(0, 0, 0, 0.75);
padding: 15px;
}
h1 {
font-size: 40px;
font-family: Tahoma;
font-weight: 900;
}
/*закончили со стилями*/
</style>
<!-- закрываем служебную часть страницы -->
</head>
<!-- началось содержимое страницы -->
<body>
<!-- началась видимая часть -->
<!-- заголовок страницы -->
<h1>Текстовый редактор с автосохранением</h1>
<!-- большой блок для ввода текста: высота в половину, а ширина — во весь экран, назвывается "text_area", обведено рамкой толщиной в 1 пиксель, выравнивание текста — по левому краю -->
<div id="editor" contenteditable="true" class="editorSheet">
</div>
<!-- закончилась видимая часть -->
<!-- пишем скрипт, который будет постоянно сохранять наш текст -->
<script>
// если в нашем хранилище уже что-то есть…
if (localStorage.getItem('text_in_editor') !== null) {
// …то отображаем его содержимое в нашем редакторе
document.getElementById('editor').innerHTML = localStorage.getItem('text_in_editor');
}
// отслеживаем каждое нажатие клавиши и при каждом нажатии выполняем команду
document.addEventListener('keydown', function (e) {
// записываем содержимое нашего редактора в хранилище
localStorage.setItem('text_in_editor', document.getElementById('editor').innerHTML);
});
// закончился скрипт
</script>
<!-- закончилось содержимое страницы -->
</body>
<!-- конец всего HTML-документа -->
</html>
Добавляем список документов
Сделаем наш список документов слева, но настроим его поведение таким образом, чтобы на маленьких экранах он занимал всю ширину браузера. Используем для этого Бутстрап, с которым мы уже познакомились, — он сам за нас сделает всю работу по адаптивной вёрстке. Сначала подключим его:
<link rel="stylesheet"
href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1
/css/bootstrap.min.css"
integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH
/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T"
crossorigin="anonymous">
Теперь изменим код заголовка страницы — положим его в отдельный контейнер и зададим размеры:
<div class="container" >
<div class="row">
<!— делаем заголовок —>
<div class="col-12″>
<h1 id="h1_name">Текстовый редактор с автосохранением</h1>
</div>
</div>
</div>
Затем сделаем список документов. Для этого мы воспользуемся своим же готовым кодом из статьи про список задач и адаптируем его под наши цели — меняем текст, оборачиваем код в новый контейнер и задаём размер блоков для каждого размера экрана. Ниже тоже делаем отдельный блок с окном редактора, у которого размеры свои:
<div class="container">
<div class="row">
<div class="col-12 col-sm-12 col-md-4 col-lg-3 col-xl-3">
<!-- делаем боковой список с названиями документов-->
<!-- заголовок списка -->
<h2>Документы</h2>
<p style="font-size: 14px">Alt + клик — удаляет документ</p>
<!-- поле ввода, куда пишем название новых документов -->
<div id="tdlApp">
<input type="text" class="form-control" placeholder="Новый документ">
<!-- создаём пока ещё пустой список -->
<div class="tdlDiv">
<ul class="List list-unstyled">
<!-- тут появятся наши названия документов, когда мы их добавим -->
</ul>
</div>
</div>
<!-- закончили с оформлением списка -->
</div>
<!-- делаем само окно редактора, где пишем весь текст -->
<div class="col-12 col-sm-12 col-md-8 col-lg-9 col-xl-9">
<div id="editor" contenteditable="true" class="editorSheet">
</div>
</div>
</div>
</div>
Как видите, мы взяли наш старый рабочий код, немного поменяли текст в заголовках, добавили фреймворк и использовали в новой программе. Быстро и удобно.
Настраиваем внешний вид
Раз мы добавили новые теги и стили, нужно их прописать в разделе <style>
. Если этого не сделать, браузер не поймёт, как с ними работать, и программа будет делать не то, что нам нужно. Как и раньше, мы объединяем стили двух старых программ в один и слегка меняем их:
/*задаём общие параметры для всей страницы: шрифт и отступы*/
body {
text-align: left;
margin: 10;
font-family: Courier New, Courier;
font-size: 20px;
background-color: lightgray;
}
/* оформляем окно редактора */
.editorSheet {
min-height: 65vw;
border: solid;
border-width: 0px;
text-align: left;
background-color: white;
-webkit-box-shadow: 6px 10px 9px 0px rgba(0, 0, 0, 0.75);
-moz-box-shadow: 6px 10px 9px 0px rgba(0, 0, 0, 0.75);
box-shadow: 6px 10px 9px 0px rgba(0, 0, 0, 0.75);
padding: 15px;
border-style: solid;
border-width: 1px;
}
/* стиль заголовка h1*/
h1 {
font-size: 40px;
font-family: Tahoma;
font-weight: 900;
padding-bottom: 30px;
}
/* стиль заголовка h2*/
h2 {
font-size: 25px;
font-family: Arial;
font-weight: 900;
color: black;
font-weight: 400;
}
/* настраиваем внешний вид поля ввода*/
input {
display: inline-block;
margin: 20px auto;
border: 2px solid #eee;
padding: 10px 20px;
font-family: Verdana, Arial, sans-serif;
font-size: 13px;
}
/*как будет выглядеть каждый элемент нашего списка документов*/
.tdItem {
text-align: left;
padding: 10px;
cursor: default;
border-radius: 7px;
font-size: 16px;
}
/*что произойдёт, когда мы наведём курсор на название документа в списке*/
.tdItem:hover {
background-color: lightblue;
}
Сохраняем изменения в исходном коде нашего редактора, обновляем и смотрим на результат:
Собираем скрипт
Главная задача в скрипте — постоянно отслеживать открытый документ и показывать его содержимое. Для этого заведём переменную N_store, где будем хранить внутренний номер документа. Остальное как обычно — собираем один скрипт из двух готовых:
// заводим переменные под наши задачи
var List = $('#tdlApp ul');
var Mask = 'tdl_';
// тут будем хранить внутренний номер документа, чтобы связать список и текст в редакторе
var N_store;
// отслеживаем каждое нажатие клавиши в редакторе и при каждом нажатии выполняем команду
document.addEventListener('keydown', function (e) {
// записываем содержимое нашего редактора в хранилище, при этом ячейка связана с текущим документом
localStorage.setItem(N_store + 'text_in_editor', document.getElementById('editor').innerHTML);
});
// функция, которая берёт из памяти наши документы и делает из них список
function showTasks() {
// временно скрываем окно редактора
document.getElementById('editor').style.display = "none";
// узнаём размер хранилища
var Storage_size = localStorage.length;
// если в хранилище что-то есть…
if (Storage_size > 0) {
// то берём и добавляем это в список документов
for (var i = 0; i < Storage_size; i++) {
var key = localStorage.key(i);
if (key.indexOf(Mask) == 0) {
// запоминаем внутренний номер документа, чтобы правильно показать текст в редакторе
N_store = key[4];
// делаем содержимое хранилища элементами списка
$('<li></li>').addClass('tdItem')
.attr('data-itemid', key)
.text(localStorage.getItem(key))
.appendTo(List);
}
}
}
}
// сразу вызываем эту функцию, вдруг в памяти уже остались документы с прошлого раза
showTasks();
// следим, когда пользователь напишет название нового документа в поле ввода и нажмёт Enter
$('#tdlApp input').on('keydown', function (e) {
if (e.keyCode != 13) return;
var str = e.target.value;
e.target.value = "";
// если в поле ввода было что-то написано — начинаем обрабатывать
if (str.length > 0) {
var number_Id = 0;
List.children().each(function (index, el) {
var element_Id = $(el).attr('data-itemid').slice(4);
if (element_Id > number_Id)
number_Id = element_Id;
})
number_Id++;
// отправляем новый документ сразу в память
localStorage.setItem(Mask + number_Id, str);
// готовим для него новое поле редактора
// берём текущий внутренний номер документа
N_store = number_Id;
// отправляем в память
localStorage.setItem(N_store + 'text_in_editor', '');
// делаем окно редактора видимым и очищаем текст в нём
document.getElementById('editor').innerHTML = '';
document.getElementById('editor').style.display = "block";
// добавляем название документа в конец списка
$('<li></li>').addClass('tdItem')
.attr('data-itemid', Mask + number_Id)
.text(str).appendTo(List);
// меняем заголовок редактора
document.getElementById('h1_name').innerHTML = localStorage.getItem('tdl_' + N_store);
}
});
Теперь осталось самое интересное — обрабатывать разные клики на названии документов в списке. Чтобы различать клик с нажатым Альтом и без него, используем свойство <event.altKey>
— оно покажет, был ли Альт нажат в момент события. Если да — удаляем документ. В той же функции обработаем и простой клик по названию:
// при клике на названии документа — делаем его активным и даём с ним работать
$(document).on('click', '.tdItem', function (e) {
// находим документ, по которому кликнули
var jet = $(e.target);
// если при клике был нажат Atl
if (event.altKey) {
// то убираем документ из памяти
localStorage.removeItem(jet.attr('data-itemid'));
localStorage.removeItem(jet.attr('data-itemid')[4] + 'text_in_editor');
// очищаем и скрываем окно редактора
document.getElementById('editor').innerHTML = '';
document.getElementById('editor').style.display = "none";
// меняем заголовок редактора
document.getElementById('h1_name').innerHTML = 'Текстовый редактор с автосохранением';
// и убираем документ из списка
jet.remove();
// выходим из функции, чтобы не обрабатывать обычный клик
return true;
}
// обрабатываем обычный клик — делаем документ активным
// получаем внутренний номер документа
N_store = jet.attr('data-itemid')[4];
// делаем окно редактора видимым и заполняем его содержимым из памяти
document.getElementById('editor').style.display = "block";
document.getElementById('editor').innerHTML = localStorage.getItem(N_store + 'text_in_editor');
// меняем заголовок редактора
document.getElementById('h1_name').innerHTML = localStorage.getItem('tdl_' + N_store);
})
Собираем всё в один файл и проверяем, что у нас подключён jQuery, который и отвечает почти за всю магию работы с элементами на странице. Обновляем и наслаждаемся результатом:
Общий код страницы
<!DOCTYPE html>
<html>
<!-- служебная часть -->
<head>
<!-- заголовок страницы -->
<title>Прокачанный текстовый редактор</title>
<!-- настраиваем служебную информацию для браузеров -->
<meta 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="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css"
integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">
<!-- задаём CSS-стили прямо здесь же, чтобы всё было в одном файле -->
<style type="text/css">
/*задаём общие параметры для всей страницы: шрифт и отступы*/
body {
text-align: left;
margin: 10;
font-family: Courier New, Courier;
font-size: 20px;
background-color: lightgray;
}
/* оформляем окно редактора */
.editorSheet {
min-height: 65vw;
border: solid;
border-width: 0px;
text-align: left;
background-color: white;
-webkit-box-shadow: 6px 10px 9px 0px rgba(0, 0, 0, 0.75);
-moz-box-shadow: 6px 10px 9px 0px rgba(0, 0, 0, 0.75);
box-shadow: 6px 10px 9px 0px rgba(0, 0, 0, 0.75);
padding: 15px;
border-style: solid;
border-width: 1px;
}
/* стиль заголовка h1*/
h1 {
font-size: 40px;
font-family: Tahoma;
font-weight: 900;
padding-bottom: 30px;
}
/* стиль заголовка h2*/
h2 {
font-size: 25px;
font-family: Arial;
font-weight: 900;
color: black;
font-weight: 400;
}
/* настраиваем внешний вид поля ввода*/
input {
display: inline-block;
margin: 20px auto;
border: 2px solid #eee;
padding: 10px 20px;
font-family: Verdana, Arial, sans-serif;
font-size: 13px;
}
/*как будет выглядеть каждый элемент нашего списка документов*/
.tdItem {
text-align: left;
padding: 10px;
cursor: default;
border-radius: 7px;
font-size: 16px;
}
/*что произойдёт, когда мы наведём курсор на название документа в списке*/
.tdItem:hover {
background-color: lightblue;
}
/*закончили со стилями*/
</style>
<!-- закрываем служебную часть страницы -->
</head>
<!-- началось содержимое страницы -->
<body>
<!-- началась визуальная часть -->
<div class="container">
<div class="row">
<!-- делаем заголовок -->
<div class="col-12">
<h1 id="h1_name">Текстовый редактор с автосохранением</h1>
</div>
</div>
</div>
<div class="container">
<div class="row">
<div class="col-12 col-sm-12 col-md-4 col-lg-3 col-xl-3">
<!-- делаем боковой список с названиями документов-->
<!-- заголовок списка -->
<h2>Документы</h2>
<p style="font-size: 14px">Alt + клик — удаляет документ</p>
<!-- поле ввода, куда пишем название новых документов -->
<div id="tdlApp">
<input type="text" class="form-control" placeholder="Новый документ">
<!-- создаём пока ещё пустой список -->
<div class="tdlDiv">
<ul class="List list-unstyled">
<!-- тут появятся наши названия документов, когда мы их добавим -->
</ul>
</div>
</div>
<!-- закончили с оформлением списка -->
</div>
<!-- делаем само окно редактора, где пишем весь текст -->
<div class="col-12 col-sm-12 col-md-8 col-lg-9 col-xl-9">
<div id="editor" contenteditable="true" class="editorSheet">
</div>
</div>
</div>
</div>
<!-- закончилась видимая часть -->
<!-- подключаем JQuery -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.3/jquery.min.js">
</script>
<!-- пишем скрипт, который будет постоянно сохранять наш текст -->
<script>
// заводим переменные под наши задачи
var List = $('#tdlApp ul');
var Mask = 'tdl_';
// тут будем хранить внутренний номер документа, чтобы связать список и текст в редакторе
var N_store;
// отслеживаем каждое нажатие клавиши в редакторе и при каждом нажатии выполняем команду
document.addEventListener('keydown', function (e) {
// записываем содержимое нашего редактора в хранилище, при этом ячейка связана с текущим документом
localStorage.setItem(N_store + 'text_in_editor', document.getElementById('editor').innerHTML);
});
// функция, которая берёт из памяти наши документы и делает из них список
function showTasks() {
// временно скрываем окно редактора
document.getElementById('editor').style.display = "none";
// узнаём размер хранилища
var Storage_size = localStorage.length;
// если в хранилище что-то есть…
if (Storage_size > 0) {
// то берём и добавляем это в список документов
for (var i = 0; i < Storage_size; i++) {
var key = localStorage.key(i);
if (key.indexOf(Mask) == 0) {
// запоминаем внутренний номер документа, чтобы правильно показать текст в редакторе
N_store = key[4];
// делаем содержимое хранилища элементами списка
$('<li></li>').addClass('tdItem')
.attr('data-itemid', key)
.text(localStorage.getItem(key))
.appendTo(List);
}
}
}
}
// сразу вызываем эту функцию, вдруг в памяти уже остались документы с прошлого раза
showTasks();
// следим, когда пользователь напишет название нового документа в поле ввода и нажмёт Enter
$('#tdlApp input').on('keydown', function (e) {
if (e.keyCode != 13) return;
var str = e.target.value;
e.target.value = "";
// если в поле ввода было что-то написано — начинаем обрабатывать
if (str.length > 0) {
var number_Id = 0;
List.children().each(function (index, el) {
var element_Id = $(el).attr('data-itemid').slice(4);
if (element_Id > number_Id)
number_Id = element_Id;
})
number_Id++;
// отправляем новый документ сразу в память
localStorage.setItem(Mask + number_Id, str);
// готовим для него новое поле редактора
// берём текущий внутренний номер документа
N_store = number_Id;
// отправляем в память
localStorage.setItem(N_store + 'text_in_editor', '');
// делаем окно редактора видимым и очищаем текст в нём
document.getElementById('editor').innerHTML = '';
document.getElementById('editor').style.display = "block";
// добавляем название документа в конец списка
$('<li></li>').addClass('tdItem')
.attr('data-itemid', Mask + number_Id)
.text(str).appendTo(List);
// меняем заголовок редактора
document.getElementById('h1_name').innerHTML = localStorage.getItem('tdl_' + N_store);
}
});
// при клике на названии документа — делаем его активным и даём с ним работать
$(document).on('click', '.tdItem', function (e) {
// находим документ, по которому кликнули
var jet = $(e.target);
// если при клике был нажат Atl
if (event.altKey) {
// то убираем документ из памяти
localStorage.removeItem(jet.attr('data-itemid'));
localStorage.removeItem(jet.attr('data-itemid')[4] + 'text_in_editor');
// очищаем и скрываем окно редактора
document.getElementById('editor').innerHTML = '';
document.getElementById('editor').style.display = "none";
// меняем заголовок редактора
document.getElementById('h1_name').innerHTML = 'Текстовый редактор с автосохранением';
// и убираем документ из списка
jet.remove();
// выходим из функции, чтобы не обрабатывать обычный клик
return true;
}
// обрабатываем обычный клик — делаем документ активным
// получаем внутренний номер документа
N_store = jet.attr('data-itemid')[4];
// делаем окно редактора видимым и заполняем его содержимым из памяти
document.getElementById('editor').style.display = "block";
document.getElementById('editor').innerHTML = localStorage.getItem(N_store + 'text_in_editor');
// меняем заголовок редактора
document.getElementById('h1_name').innerHTML = localStorage.getItem('tdl_' + N_store);
})
// закончился скрипт
</script>
<!-- закончилось содержимое страницы -->
</body>
<!-- конец всего HTML-документа -->
</html>