В прошлый раз мы рассказали, что такое Drag-and-drop и как работает такая форма загрузки файлов на сайт. Сегодня мы сделаем такую форму на своём сайте.
Что делаем
Форма Drag-and-Drop позволяет загружать файлы на сайт путём перетаскивания их в специальную область на экране. С такой формой интерфейс удобнее, чем с кнопкой «Обзор», по нажатию которой открывается проводник всех файлов на компьютере. Но на всякий случай мы добавим и такую кнопку. Пока что займёмся визуальной составляющей:
- Подготовим страницу к тому, чтобы она умела принимать файлы перетаскиванием.
- Придадим этому процессу симпатичный внешний вид.
- Научим страницу обрабатывать файлы и отправлять на нужный сервер.
Чтобы наша форма загружала файлы на сервер, нужно будет настроить серверную часть — это мы сделаем в следующий раз.
Чтобы сделать форму Drag-and-drop, нам понадобятся:
- HTML-страница с нужными элементами.
- CSS-стили, чтобы форма выглядела симпатично.
- Код JavaScript для взаимодействия элементов между собой и связки формы с сервером.
Всё это мы сохраним в отдельных файлах в одной папке. Так страница будет знать, откуда что брать.
Делаем страницу с формой
Создадим стандартный HTML-файл:
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Форма Drag-and-Drop</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
</body>
</html>
Сразу укажем путь к файлу со стилями. Его пока нет, но мы создадим его чуть позже. У нас получилась пустая страница, добавим в неё нужные элементы.
Чтобы добавить форму загрузки, используем тег <div> — он создаст блок внутри страницы, с которым мы будем работать дальше. Ещё нам понадобится указать id, чтобы к этому блоку мог обращаться код JavaScript, и класс для применения стилей CSS:
<!-- общий класс для всего блока формы -->
<div id="uploadFile_Loader" class="upload-zone">
</div>
Теперь в блок добавим форму — с её помощью мы и будем отправлять загруженные файлы на сервер. В форме мы тоже указываем id и класс, а ещё способ отправки файлов на сервер и тип кодирования, чтобы браузер понимал, как ему обработать файлы перед отправкой.
<!-- класс для формы отправки -->
<form class="form-upload" id="uploadForm" method="post" enctype="multipart/form-data">
Наконец, наполним нашу форму нужными элементами:
- текстом с пояснением, что нужно перетаскивать файлы именно в эту область;
- кнопкой загрузки, если пользователь не хочет или не может перетащить файл;
- дополнительным классом, чтобы область для перетаскивания меняла цвет при перетаскивании;
- кнопкой для отправки на сервер.
Вот как это выглядит в коде:
<!-- область для перетаскивания файлов -->
<div class="upload-zone_dragover">
<!-- текст внутри области для перетаскивания -->
<p>Перетащите файл сюда</p>
</div>
<!-- кнопка отправки на сервер -->
<button type="submit" class="form-upload__submit">Отправить</button>
<!-- указываем, к какому элементу будет относиться подсказка для кнопки -->
<label class="form-upload__label" for="uploadForm_file">
<!-- текст подсказки для кнопки -->
<span class="form-upload__title">Или выберите:</span>
<!-- кнопка выбора файла -->
<input class="form-upload__input" id="uploadForm_File" type="file" name="file_name">
</label>
<!-- кнопка отправки на сервер -->
<button type="submit" class="form-upload__submit">Отправить</button>
Обратите внимание, как мы сделали кнопку для выбора файла:
- Задали тег input, который обозначает элементы ввода.
- Указали, что кнопка отвечает за загрузку файла (type="file").
- Задали имя, которое получит сервер при отправке формы (name="file_name").
С кнопкой отправки всё проще, потому что мы пропишем её поведение в коде JavaScript.
В итоге получилась такая форма. Выглядит не очень — у нас пока нет дизайна:
Настраиваем стили
Чтобы форма выглядела лучше, настроим стили. Для этого создадим файл style.css, который потом подключим к нашей html-странице.
Сначала зададим стиль для всей области: внутри неё будет область для перетаскивания файлов, кнопка загрузки и подсказка для кнопки:
/* внешний вид всей области */
.form-upload {
/* делаем область гибкой, подстраиваемой под ширину под ширину внутренних элементов */
display: inline-flex;
/* все элементы внутри выстраиваем в столбец */
flex-direction: column;
/* центрируем внутренние элементы по горизонтали */
align-items: center;
/* центрируем элементы по вертикали */
justify-content: center;
/* область занимает 100% ширины окна браузера */
width: 100%;
/* область занимает 100% высоты окна браузера */
min-height: 100vh;
/* внутренние отступы */
padding: 16px;
}
Для удобства мы применяем модуль CSS, который называется Flexbox. Он позволяет блоку растягиваться под размеры внутренних элементов, экраны разных устройств и браузеров. Например, display: inline-flex — это Flexbox.
Наша страница немного преобразилась:
Теперь настроим область, в которую нужно перетаскивать файлы:
/* область для перетаскивания файлов */
.upload-zone_dragover {
/* цвет фона */
background-color: #1e96ff;
/* форма подстраивается под размеры элементов */
display: inline-flex;
/* центрируем внутренние элементы по горизонтали */
align-items: center;
/* центрируем элементы по вертикали */
justify-content: center;
/* центрируем элементы по вертикали */
justify-content: center;
/* ширина */
width: 450px;
/* высота */
height: 250px;
/* внутренние отступы */
padding: 16px;
/* скругление рамки */
border-radius: 10px;
/* цвет текста */
color: #ffffff;
/* размер текста */
font-size: 24px;
/* тень с размытием */
box-shadow: 3px 3px 4px rgba(0, 0, 0, 0.2);
}
Область для перетаскивания файлов стала симпатичнее:
Настроим область для подсказки к кнопке загрузки и саму кнопку:
/* зону с кнопкой и подписью к ней */
.form-upload__label {
/* область занимает всю область элемента, внутри которого находится */
display: flex;
/* подпись над кнопкой */
flex-direction: column;
/* центрируем внутренние элементы по горизонтали */
align-items: center;
}
/* подпись к кнопке */
.form-upload__title {
/* размер шрифта */
font-size: 16px;
/* насыщенность шрифта */
font-weight: 500;
/* шрифт */
font-family: Arial, sans-serif;
/* отступ над текстом */
margin-top: 8px;
/* отступ под текстом */
margin-bottom: 8px;
}
Настроим оформление для самой кнопки:
/* кнопка выбора файла */
.form-upload__label input[type="file"]::file-selector-button {
/* убираем рамку */
border: none;
/* скругляем углы */
border-radius: 8px;
/* размер шрифта */
font-size: 16px;
/* шрифт и его цвет */
font-family: Arial, sans-serif;
color: #000000;
/* внутренние отступы */
padding: 8px 16px;
}
Получилось так:
Добавим класс, чтобы кнопка реагировала при наведении:
/* цвет кнопки при наведении на неё курсора */
.form-upload__label input[type="file"]::file-selector-button:hover {
background-color: #FD7F4A;
}
Теперь, когда мы наводим курсор на кнопку выбора файла, она меняет цвет:
У текста, которым будет написано название файла, добавленного в форму, тоже можно задать стили:
/* шрифт названия файла */
.form-upload__input {
/* выставляем шрифт и его размер */
font-family: Arial, sans-serif;
font-size: 16px;
}
Теперь так же оформим кнопку для отправки файлов на сервер.
/* стили для кнопки отправки формы */
button[type="submit"] {
/* отступ над текстом */
margin-top: 8px;
/* цвет фона */
background-color: #c4c4c4;
/* цвет текста */
color: white;
/* внутренние отступы кнопки */
padding: 12px 20px;
/* убираем границу */
border: none;
/* добавляем скругление */
border-radius: 8px;
/* размер шрифта */
font-size: 18px;
/* шрифт */
font-family: Arial, sans-serif;
/*цвет шрифта */
color: #000000;
}
/* стили для кнопки отправки формы при наведении */
button[type="submit"]:hover {
/* цвет фона */
background-color: #45a049;
}
Теперь кнопка отправки тоже меняет цвет, если навести на неё курсор мыши:
Добавим другой цвет для области загрузки во время перетаскивания файла в неё:
/* стили для состояния, когда над формой держат файл */
.upload-zone_dragover._active {
/* цвет текста */
color: #000000;
/* цвет фона */
background-color: #00ff51be;
}
Вот что получилось. Форма выглядит уже лучше и даже умеет принимать файлы, но пока на этом всё. Надо научить её работать.
Учим форму отправлять файлы на сервер
Создадим файл script.js. Сначала скрипту нужно объяснить, с какими элементами из HTML-файла мы будем работать. Для этого мы создадим переменные, в которые положим id из HTML-кода страницы.
// запоминаем область перетаскивания файла
const dropFileZone = document.querySelector(".upload-zone_dragover");
// запоминаем кнопку добавления файла
const uploadInput = document.querySelector(".form-upload__input");
// запоминаем кнопку отправки
const submitButton = document.querySelector('.form-upload__submit');
Теперь в отдельную переменную запишем адрес сервера, куда форма будет отправлять файлы. У нас это будет несуществующий адрес, потому что сервера пока нет:
// записываем в переменную адрес, куда отправятся файлы после загрузки
const uploadUrl = "/unicorns";
Добавим обработчики событий. Это функции, которые будут говорить коду, что делать в конкретной ситуации. Сейчас добавим две: когда файл перетаскивают над формой и когда файл положили в форму.
Как только файл окажется в форме и браузер это увидит, он попытается его открыть. Нам это не нужно, потому что мы хотим отправить его на сервер позже по нажатию кнопки.
// добавляем обработчики событий "dragover" и "drop" для документа
["dragover", "drop"].forEach(function (event) {
// блокируем стандартное поведение браузера для события и возвращаем false
document.addEventListener(event, function (evt) {
evt.preventDefault();
return false;
});
});
Научим форму менять цвет, когда над ней проносят файл и когда файл уходит за границы формы:
// добавляем обработчик события для входа в зону перетаскивания файла"
dropFileZone.addEventListener("dragenter", function () {
// добавляем класс стиля, красим форму
dropFileZone.classList.add("_active");
});
// добавляем обработчик события для выхода из зоны перетаскивания файла"
dropFileZone.addEventListener("dragleave", function () {
// возвращаем цвет неактивной формы"
dropFileZone.classList.remove("_active");
});
Теперь нужно подготовить файл к отправке. Нам понадобятся два обработчика событий: один для подготовки файла из зоны перетаскивания и ещё один — для подготовки файла, который выбрали для загрузки через кнопку.
// добавляем обработчик события "drop" для зоны перетаскивания"
dropFileZone.addEventListener("drop", function () {
// удаляем класс активности при сбросе файла
dropFileZone.classList.remove("_active");
// получаем первый файл в списке
const file = event.dataTransfer?.files[0];
// проверяем, что файл есть
if (file) {
// готовим файл к отправке
uploadInput.files = event.dataTransfer.files;
}
});
// добавляем обработчик события для файлов, добавленных кнопкой"
uploadInput.addEventListener("change", (event) => {
// получаем первый файл в списке
const file = uploadInput.files?.[0];
// проверяем, что файл есть
if (file) {
// готовим файл к отправке
uploadInput.files = event.dataTransfer.files;
}
});
У нас есть кнопка отправки, но пока что наш код не знает, что делать при её нажатии. Исправляем это через последний обработчик события:
// добавляем обработчик события "click" для кнопки отправки
submitButton.addEventListener("click", function (event) {
// блокируем стандартное поведение кнопки (отправку формы)
event.preventDefault();
// вызываем функцию для отправки файла
processingUploadFile();
});
Пишем функцию для отправки данных на сервер
Чтобы файл из формы отправился на сервер, а страница при этом не перезагрузилась, используют технологию XMLHttpRequest. Это специальный API для обмена данными между сервером и клиентом. Благодаря XMLHttpRequest обновляется только часть веб-страницы. Эта технология часто используется в одностраничных приложениях.
Логика такая:
- Файл из формы сохраняется для отправки.
- Выполняется запрос к серверу.
- Файл отправляется на сервер.
- Добавляются уведомление для пользователя о статусе загрузки.
Получается такая функция:
// функция для обработки загрузки файла
function processingUploadFile(file) {
// проверяем, что файл был отправлен
if (file) {
// создаём объект для отправки данных формы
const dropZoneData = new FormData();
// создаём объект для отправки запроса на сервер
const xhr = new XMLHttpRequest();
// добавляем файл из формы в объект FormData
dropZoneData.append("file", file);
// открываем соединение с сервером
xhr.open("POST", uploadUrl, true);
// отправляем данные на сервер
xhr.send(dropZoneData);
// устанавливаем обработчик события onload для выполнения действий после завершения загрузки
xhr.onload = function () {
// проверяем статус ответа сервера
if (xhr.status == 200) {
// сообщаем об успехе в консоли"
console.log("Всё загружено");
} else {
// соообщаем об ошибке в консоли"
console.log("Ошибка загрузки");
}
// скрываем элемент
HTMLElement.style.display = "none";
};
}
}
Что дальше
У нас получилась простая форма для загрузки файлов с помощью перетаскивания Drag-and-Drop. А вот что можно в ней улучшить:
- сделать красивый интерфейс;
- добавить возможность загрузки нескольких файлов;
- научить форму очищать список файлов;
- настроить отправку на сервер несколькими способами, чтобы наверняка.
А ещё, чтобы форма действительно отправляла файлы, нужна серверная часть. У нас её пока нет.
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Форма Drag-and-Drop</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<div id="uploadFile_Loader" class="upload-zone">
<form class="form-upload" id="uploadForm" method="post" enctype="multipart/form-data">
<!-- область для перетаскивания файлов -->
<div class="upload-zone_dragover">
<!-- текст внутри области для перетаскивания -->
<p>Перетащите файл сюда</p>
</div>
<!-- указываем, к какому элементу будет относиться подсказка для кнопки -->
<label class="form-upload__label" for="uploadForm_file">
<!-- подсказка для нажатия кнопки -->
<span class="form-upload__title">Или выберите
<!-- кнопка выбора файла -->
<input class="form-upload__input" id="uploadForm_File" type="file" name="file_name">
</span>
</label>
<!-- кнопка отправки на сервер -->
<button type="submit" class="form-upload__submit">Отправить</button>
</form>
</div>
<script src="script.js"></script>
</body>
</html>
/* внешний вид всей области */
.form-upload {
/* область подстраиваится под ширину внутренних элементов */
display: inline-flex;
/* выстраиваем все элементы внутри в столбец */
flex-direction: column;
/* центрируем внутренние элементы по горизонтали */
align-items: center;
/* центрируем элементы по вертикали */
justify-content: center;
/* область занимает 100% ширины окна браузера */
width: 100%;
/* область занимает 100% высоты окна браузера */
min-height: 100vh;
/* выставляем внутренние отступы */
padding: 16px;
}
/* область для перетаскивания файлов */
.upload-zone_dragover {
/* цвет фона */
background-color: #4061EA;
/* форма подстраивается под размеры элементов */
display: inline-flex;
/* центрируем внутренние элементы по горизонтали */
align-items: center;
/* центрируем элементы по вертикали */
justify-content: center;
/* центрируем элементы по вертикали */
justify-content: center;
/* ширина */
width: 450px;
/* высота */
height: 250px;
/* внутренние отступы */
padding: 16px;
/* скругление рамки */
border-radius: 10px;
/* цвет текста */
color: #ffffff;
/* размер текста */
font-size: 24px;
/* тень с размытием */
box-shadow: 3px 3px 4px rgba(0, 0, 0, 0.2);
}
/* область с кнопкой и подпись к ней */
.form-upload__label {
/* область занимает всю область элемента, внутри которого находится */
display: flex;
/* подпись к кнопке */
flex-direction: column;
/* центрируем внутренние элементы по горизонтали */
align-items: center;
}
/* подпись к кнопке */
.form-upload__title {
/* размер шрифта */
font-size: 16px;
/* насыщенность шрифта */
font-weight: 500;
/* шрифт */
font-family: Arial, sans-serif;
/* отступ над текстом */
margin-top: 8px;
/* отступ под текстом */
margin-bottom: 8px;
}
/* кнопка выбора файла */
.form-upload__label input[type="file"]::file-selector-button {
/* убираем рамку */
border: none;
/* скругляем углы */
border-radius: 8px;
/* размер шрифта */
font-size: 16px;
/* шрифт и его цвет */
font-family: Arial, sans-serif;
color: #000000;
/* внутренние отступы */
padding: 8px 16px;
}
/* цвет кнопки выбора файла при наведении на неё курсора */
.form-upload__label input[type="file"]::file-selector-button:hover {
background-color: #FD7F4A;
}
/* шрифт названия файла */
.form-upload__input {
/* шрифт */
font-family: Arial, sans-serif;
/* размер шрифта */
font-size: 16px;
}
/* стили для кнопки отправки формы */
button[type="submit"] {
/* отступ над текстом */
margin-top: 8px;
/* цвет фона */
background-color: #c4c4c4;
/* цвет текста */
color: white;
/* внутренние отступы */
padding: 12px 20px;
/* убираем границу */
border: none;
border-radius: 8px;
/* размер шрифта */
font-size: 18px;
/* шрифт */
font-family: Arial, sans-serif;
/* цвет шрифта */
color: #000000;
/* курсор в виде указателя при наведении */
cursor: pointer;
}
/* стили для кнопки отправки формы при наведении */
button[type="submit"]:hover {
/* цвет фона кнопки при наведении */
background-color: #D4FB51;
}
/* стили для состояния, когда над формой держат файл */
.upload-zone_dragover._active {
/* цвет текста */
color: #000000;
/* цвет фона */
background-color: #D4FB51;
}
// запоминаем область перетаскивания файла
const dropFileZone = document.querySelector(".upload-zone_dragover");
// запоминаем кнопку добавления файла
const uploadInput = document.querySelector(".form-upload__input");
// запоминаем кнопку отправки
const submitButton = document.querySelector('.form-upload__submit');
// записываем в переменную адрес, куда отправятся файлы после загрузки
const uploadUrl = "/unicorns";
// добавляем обработчики событий "dragover" и "drop" для документа
["dragover", "drop"].forEach(function (event) {
// блокируем стандартное поведение браузера для события и возвращаем false
document.addEventListener(event, function (evt) {
evt.preventDefault();
return false;
});
});
// добавляем обработчик события для входа в зону перетаскивания файла"
dropFileZone.addEventListener("dragenter", function () {
// добавляем класс стиля, красим форму
dropFileZone.classList.add("_active");
});
// добавляем обработчик события для выхода из зоны перетаскивания файла"
dropFileZone.addEventListener("dragleave", function () {
// возвращаем цвет неактивной формы"
dropFileZone.classList.remove("_active");
});
// добавляем обработчик события "drop" для зоны перетаскивания"
dropFileZone.addEventListener("drop", function () {
// удаляем класс активности при сбросе файла
dropFileZone.classList.remove("_active");
// получаем первый файл в списке
const file = event.dataTransfer?.files[0];
// проверяем, что файл есть
if (file) {
// готовим файл к отправке
uploadInput.files = event.dataTransfer.files;
}
});
// добавляем обработчик события для файлов, добавленных кнопкой"
uploadInput.addEventListener("change", (event) => {
// получаем первый файл в списке
const file = uploadInput.files?.[0];
// проверяем, что файл есть
if (file) {
// готовим файл к отправке
uploadInput.files = event.dataTransfer.files;
}
});
// добавляем обработчик события "click" для кнопки отправки
submitButton.addEventListener("click", function (event) {
// блокируем стандартное поведение кнопки (отправку формы)
event.preventDefault();
// вызываем функцию для отправки файла
processingUploadFile();
});
// функция для обработки загрузки файла
function processingUploadFile(file) {
// проверяем, что файл был отправлен
if (file) {
// создаём объект для отправки данных формы
const dropZoneData = new FormData();
// создаём объект для отправки запроса на сервер
const xhr = new XMLHttpRequest();
// добавляем файл из формы в объект FormData
dropZoneData.append("file", file);
// открываем соединение с сервером
xhr.open("POST", uploadUrl, true);
// отправляем данные на сервер
xhr.send(dropZoneData);
// устанавливаем обработчик события onload для выполнения действий после завершения загрузки
xhr.onload = function () {
// проверяем статус ответа сервера
if (xhr.status == 200) {
// сообщаем об успехе в консоли"
console.log("Всё загружено");
} else {
// соообщаем об ошибке в консоли"
console.log("Ошибка загрузки");
}
// скрываем элемент
HTMLElement.style.display = "none";
};
}
}