js обработка отправки формы

Отправка форм при помощи JavaScript

Формы не всегда формы

В современных веб-приложениях, одностраничных приложениях и приложениях на основе фреймворков, обычно HTML-формы используются для отправки данных без загрузки нового документа при получении данных ответа. В начале поговорим о том почему это требует другого подхода.

Получение контроля над глобальным интерфейсом

Отправка стандартной HTML формы, как описывалось в предыдущей статье, загружает URL-адрес, по которому были отправлены данные, это означает, что окно браузера перемещается с полной загрузкой страницы. Если избегать полную перезагрузку страницы, можно обеспечить более плавную работу, за счёт предотвращения задержек в сети и возможных визуальных проблем (например, мерцания).

Многие современные пользовательские интерфейсы используют HTML формы только для сбора пользовательского ввода, а не для для отправки данных. Когда пользователь пытается отправить свои данные, приложение берёт контроль и асинхронно передаёт данные в фоновом режиме, обновляя только ту часть всего интерфейса пользователя, которой требуется обновление.

Асинхронная отправка произвольных данных обычно называется AJAX, что означает «Asynchronous JavaScript And XML» (Асинхронный JavaScript и XML).

Чем он отличается?

Замечание: Сейчас Fetch API часто используется вместо XHR — это современная, обновлённая версия XHR, которая работает в похожем стиле, но имеет несколько преимуществ. Большая часть XHR-кода, которую вы увидите в этой статье можно заменить на Fetch.

Если вы управляете фронтендом (кодом, который выполняется в браузере) и бэкендом (кодом, который выполняется на стороне сервера), вы можете отправлять JSON/XML и обрабатывать их как хотите.

Но если вы хотите использовать сторонний сервис, то вам необходимо отправлять данные в формате, который требуется сервису.

Так как нам следует отправлять подобные данные? Ниже описаны различные необходимые вам техники.

Отправка данных формы

Есть три способа отправки данных формы:

Источник

Выразительный JavaScript: Формы и поля форм

Содержание

Я нынче ж на ученом кутеже
Твое доверье службой завоюю,
Ты ж мне черкни расписку долговую,
Чтоб мне не сомневаться в платеже.

Мефистофель, в «Фаусте» Гёте

Формы были кратко представлены в предыдущей главе в качестве способа передачи информации, введённой пользователем, через HTTP. Они были разработаны в вебе до появления JavaScript, с тем расчётом, что взаимодействие с сервером происходит при переходе на другую страницу.

Но их элементы являются частями DOM, как и остальные части страницы, а элементы DOM, представляющие поля формы, поддерживают несколько свойств и событий, которых нет у других элементов. Это делает возможным просматривать и управлять полями ввода из программ JavaScript и добавлять функциональности к классическим формам или использовать формы и поля как основу для построения приложения.

Кнопка с атрибутом type равным submit при нажатии отправляет форму. Нажатие клавиши Enter внутри поля формы имеет тот же эффект.

Отправка формы обычно означает, что браузер переходит на страницу, обозначенную в атрибуте формы action, используя либо GET либо POST запрос. Но перед этим запускается свойство “submit”. Его можно обработать в JavaScript, и обработчик может предотвратить поведение по умолчанию, вызвав на объекте event preventDefault.

Перехват событий “submit” полезен в нескольких случаях. Мы можем написать код, проверяющий допустимость введённых значений и сразу же показать ошибку вместо передачи данных формы. Или мы можем отключить отправку формы по умолчанию и дать программе возможность самой обработать ввод, например используя XMLHttpRequest для отправки данных на сервер без перезагрузки страницы.

Текстовые поля

Свойства текстовых полей selectionStart и selectionEnd содержат данные о положении курсора и выделения текста. Когда ничего не выделено, их значение одинаковое, и равно положению курсора. Например, 0 обозначает начало текста, 10 обозначает, что курсор находится после 10-го символа. Когда выделена часть поля, свойства имеют разные значения, а именно начало и конец выделенного текста. В эти поля также можно записывать значение.

К примеру, представьте, что вы пишете статью про Khasekhemwy, но затрудняетесь писать его имя правильно. Следующий код назначает тегу

Функция replaceSelection заменяет текущий выделенный текст заданным словом, и перемещает курсор на позицию после этого слова, чтобы можно было продолжать печатать.

Событие “change” для текстового поля не срабатывает каждый раз при вводе одного символа. Оно срабатывает после потери полем фокуса, когда его значение было изменено. Чтобы мгновенно реагировать на изменение текстового поля нужно зарегистрировать событие “input”, которое срабатывает каждый раз при вводе символа, удалении текста или других манипуляциях с содержимым поля.

В следующем примере мы видим текстовое поле и счётчик, показывающий текущую длину введённого текста.

Галочки и радиокнопки

Поле галочки – простой бинарный переключатель. Его значение можно извлечь или поменять через свойство checked, содержащее булевскую величину.

Тег используется для связи куска текста с полем ввода. Атрибут for должен совпадать с id поля. Щелчок по метке label включает поле ввода, оно получает фокус и меняет значение – если это галочка или радиокнопка.

Радиокнопка схожа с галочкой, но она связана с другими радиокнопками с тем же именем, так что только одна из них может быть выбрана.

Метод document.getElementsByName выдаёт все элементы с заданным атрибутом name. Пример перебирает их (посредством обычного цикла for, а не forEach, потому что возвращаемая коллекция – не настоящий массив) и регистрирует обработчик событий для каждого элемента. Помните, что у объектов событий есть свойство target, относящееся к элементу, который запустил событие. Это полезно для создания обработчиков событий – наш обработчик может быть вызван разными элементами, и у него должен быть способ получить доступ к текущему элементу, который его вызвал.

Поля select

В большинстве браузеров внешний вид поля будет отличаться от поля с единственным вариантом выбора, которое обычно выглядит как выпадающее меню.

Атрибут size тега = 0

Файловое поле

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

Файловое поле обычно выглядит как кнопка с надписью вроде «Выберите файл», с информацией про выбранный файл рядом с ней.

Свойство files элемента – массивоподобный объект (не настоящий массив), содержащий список выбранных файлов. Изначально он пуст. У элемента нет простого свойства file, потому что пользователь может выбрать несколько файлов за раз при включённом атрибуте multiple.

У объектов в свойстве files есть свойства имя (имя файла), размер (размер файла в байтах), и тип (тип файла в смысле media type — text/plain или image/jpeg).

Чего у него нет, так это свойства, содержащего содержимое файла. Чтобы получить содержимое, приходится постараться. Так как чтение файла с диска занимает длительное время, интерфейс должен быть асинхронным, чтобы документ не замирал. Конструктор FileReader можно представлять себе, как конструктор XMLHttpRequest, только для файлов.

Чтение файла происходит при помощи создания объекта FileReader, регистрации обработчика события “load” для него, и вызова его метода readAsText с передачей тому файла. По окончании загрузки в свойстве result сохраняется содержимое файла.

Пример использует Array.prototype.forEach для прохода по массиву, так как в обычном цикле было бы неудобно получать нужные объекты file и reader от обработчика событий. Переменные были бы общими для всех итераций цикла.

Объекты FileReader также инициируют событие “error”, когда чтение файла не получается. Объект error будет сохранён в свойстве error. Если вы не хотите забивать голову ещё одной неудобной асинхронной схемой, вы можете обернуть её в обещание (см. главу 17):

Возможно читать только часть файла, вызывая slice и передавая результат (т.н. объект blob) объекту reader.

Хранение данных на стороне клиента

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

Когда такому приложению нужно сохранять информацию между сессиями, переменные JavaScript использовать не получится – их значения выбрасываются каждый раз при закрытии страницы. Можно было бы настроить сервер, подсоединить его к интернету и тогда приложение хранило бы ваши данные там. Это мы разберём в главе 20. Но это добавляет вам работы и сложности. Иногда достаточно хранить данные в своём браузере. Но как?

Можно хранить строковые данные так, что они переживут перезагрузку страниц — для этого надо положить их в объект localStorage. Он разрешает хранить строковые данные под именами (которые тоже являются строками), как в этом примере:

Переменная в localStorage хранится, пока её не перезапишут, удаляется при помощи removeItem или очисткой локального хранилища пользователем.

У сайтов с разных доменов – разные отделения в этом хранилище. То есть, данные, сохранённые с вебсайта в localStorage, могут быть прочтены или перезаписаны только скриптами с этого же сайта.

Также браузеры ограничивают объём хранимых данных, обычно в несколько мегабайт. Это ограничение, вкупе с тем фактом, что забивание жёстких дисков у людей не приносит прибыли, предотвращает отъедание места на диске.

Следующий код реализует простую программу для ведения заметок. Она хранит заметки в виде объекта, ассоциируя заголовки с содержимым. Он кодируется в JSON и хранится в localStorage. Пользователь может выбрать записку через поле новая

Скрипт инициализирует переменную notes значением из localStorage, а если его там нет – простым объектом с одной пустой записью «что купить». Попытка прочесть отсутствующее поле из localStorage вернёт null. Передача null в JSON.parse заставит его проанализировать строку «null» и вернуть null обратно. Поэтому для значения по умолчанию можно использовать оператор ||.

Когда данные в note меняются (добавляется новая запись или меняется текущая), для обновления хранимого поля вызывается функция saveToStorage. Если б мы рассчитывали, что у нас будут храниться тысячи записей, это было бы слишком накладно, и нам пришлось бы придумать более сложную процедуру для хранения – например, своё поле для каждой записи.

Когда пользователь добавляет запись, код должен обновить текстовое поле, хотя у поля и есть обработчик “change”. Это нужно потому, что событие “change” происходит, только когда пользователь меняет значение поля, а не когда это делает скрипт.

Есть ещё один похожий на localStorage объект под названием sessionStorage. Разница между ними в том, что содержимое sessionStorage забывается по окончанию сессии, что для большинства браузеров означает момент закрытия.

HTML предоставляет множество различных типов полей формы – текстовые, галочки, множественного выбора, выбора файла.

Из JavaScript можно получать значение и манипулировать этими полями. По изменению они запускают событие “change”, по вводу с клавиатуры – “input”, и ещё много разных клавиатурных событий. Они помогают нам отловить момент, когда пользователь взаимодействует с полем ввода. Свойства вроде value (для текстовых полей и select) или checked (для галочек и радиокнопок) используются для чтения и записи содержимого полей.

Когда пользователь выбрал файл в своей локальной файловой системе через поле выбора файла, интерфейс FileReader позволит нам добраться до содержимого файла из программы JavaScript.

Объекты localStorage и sessionStorage можно использовать для хранения информации таким способом, который переживёт перезагрузку страницы. Первый сохраняет данные навсегда (ну или пока пользователь специально не сотрёт их), а второй – до закрытия браузера.

Упражнения

Верстак JavaScript

Сделайте интерфейс, позволяющий писать и исполнять кусочки кода JavaScript.

Сделайте кнопку рядом с Поехали

Автодополнение

Дополните текстовое поле так, что при вводе текста под ним появлялся бы список вариантов. У вас есть массив возможных вариантов, и показывать нужно те из них, которые начинаются с вводимого текста. Когда пользователь щёлкает по предложенному варианту, он меняет содержимое поля на него.

Игра «Жизнь» Конвея

Это простая симуляция жизни на прямоугольной решётке, каждый элемент которой живой или нет. Каждое поколение (шаг игры) применяются следующие правила:

— каждая живая клетка, количество живых соседей которой меньше двух или больше трёх, погибает;
— каждая живая клетка, у которой от двух до трёх живых соседей, живёт до следующего хода;
— каждая мёртвая клетка, у которой есть ровно три живых соседа, оживает.

Соседи клетки – это все соседние с ней клетки по горизонтали, вертикали и диагонали, всего 8 штук.

Обратите внимание, что правила применяются ко всей решётке одновременно, а не к каждой из клеток по очереди. То есть, подсчёт количества соседей происходит в один момент перед следующим шагом, и изменения, происходящие на соседних клетках, не влияют на новое состояние клетки.

Источник

Работа с формами

Как обрабатывать формы, валидировать их и отправлять без перезагрузки страницы с помощью JS

Время чтения: больше 15 мин

Обновлено 21 октября 2021

Кратко

Помимо стандартных средств работы с формами можно использовать JavaScript, чтобы проверять формы на валидность, получать доступ к значениям и отправлять информацию на сервер.

Трюки для работы с формами в JS проще всего показать на примере. В этой статье мы соберём форму заявки на участие в миссии по колонизации Марса. В этой форме мы немножко приправим стандартные HTML-атрибуты динамикой на JS.

Разметка и требования

Наша форма заявки на участие в миссии «Mars Once» будет состоять из шести полей. В форме мы собираем следующие данные:

В целом форма рабочая: обязательные поля не пропустят пустые значения, атрибут type проследит, чтобы вместо почты нам не прислали номер телефона, а по нажатию на кнопку валидная форма отправит все данные.

Но нам кроме всего этого хочется:

Отправка без перезагрузки

Первым делом настроим отправку формы без перезагрузки страницы.

Перезагрузка страницы — это поведение по умолчанию для отправки формы. Чтобы его предотвратить, нам нужно «перехватить» управление в момент отправки и сказать форме, что делать вместо этого.

Предотвращаем отправку данных

Чтобы «соединить» форму с нашей будущей собственной отправкой данных, мы напишем функцию, которая будет «слушать» событие отправки и реагировать на него.

Собираем данные из формы

Следующий шаг — собрать всё, что необходимо отправить.

Нам не хочется собирать каждое значение отдельно.

Вместо этого мы будем использовать возможности языка, чтобы достать все поля и элементы управления из формы. Напишем функцию serializeForm :

Аргумент функции serializeForm — это элемент формы. Именно элемент — не селектор, а конкретный узел в DOM-дереве.

Если сейчас мы вызовем эту функцию, передав туда нашу форму как аргумент, то в консоли появится список всех элементов:

Нам останется собрать имя и значение каждого из полей. Для начала, выведем имя и значение каждого элемента в консоль:

Мы получили список элементов, преобразовали его в массив и прошлись по каждому элементу. У каждого элемента получили поля name и value и вывели их в консоль.

В консоли после запуска получим вывод по каждому из полей:

Заметим, чтобы последняя строчка не имеет ни названия, ни значения. Это потому, что последний элемент, который мы проверяли — это кнопка.

На выходе в консоли получится массив из объектов с name и value :

Значения чекбоксов

Формат данных

FormData — это особый тип данных, который можно использовать для отправки данных формы на сервер.

Но так как тип FormData специально создан для работы с формами, можно сделать гораздо проще ?

Стоит отметить, что nasa-experience в таком случае попадёт в финальные данные, только если чекбокс отметили. Если его не отметить, то в финальных данных он не окажется.

Когда чекбокс nasa-experience выделен, получим такой вывод:

Когда чекбокс не выделен — такой:

Отправка на сервер

Теперь нам надо данные из формы отправить на сервер. Представим, что наш бэкенд предоставляет API-эндпоинт для сохранения данных. Попробуем отправить их.

Функция вернёт результат запроса к серверу, который мы сможем проверить на ошибки.

Теперь используем эту функцию в обработчике события отправки. Сериализуем форму и передадим её в функцию отправки. Вместо обращения напрямую к форме, будем читать её из объекта события. Форма в объекте события submit будет храниться в свойстве target :

Обработка загрузки и вывод сообщения о результате

Теперь немножко улучшим UX нашей формы. Сейчас она просто отправляет данные и ничего не сообщает пользователям. Это не круто, потому что отправителю будет непонятно, получилось ли записаться в «Mars Once» или нет.

Показываем лоадер во время отправки

У нас вместо лоадера будет просто показываться строка «Sending. »

Добавим его после кнопки и спрячем:

Вызовем эту функцию до отправки запроса, чтобы показать лоадер, и после запроса, чтобы скрыть. Лоадер будет виден до тех пор, пока запрос не завершится:

Обрабатываем успешную отправку

Давайте теперь проверять ответ сервера. Допустим, нам хочется, чтобы при успешной отправке мы показывали alert с сообщением об успешной отправке и прятали форму:

1 300w

При успешной отправке покажется это сообщение, а форма пропадёт.

Обрабатываем ошибки

Если что-то пошло не так, то мы хотим сказать пользователям об этом. Напишем функцию, которая будет вызывать alert с сообщением, которое пришлёт сервер в случае ошибки:

Мы могли бы вызвать alert сразу на месте, но лучше вынести обработку ошибки в отдельную функцию. Так, если нам захочется добавить какие-то действия по обработке ошибок, нам будет проще ориентироваться в коде.

2 300w

Если что-то пошло не так, мы увидим причину. Форма останется на месте.

Блокируем кнопку отправки на невалидной форме

Сейчас кнопку отправки можно нажать в любой момент, даже если форма невалидна. И хоть пользователь не сможет отправить форму из-за HTML-валидации, было бы неплохо предупредить, что кнопку нажимать пока рано.

Давайте будем её блокировать до тех пор, пока не будут заполнены все поля, которые требуется заполнить.

Напишем функцию, которая будет проверять валидность формы и блокировать кнопку, если требуется. Аргументом она будет принимать событие ввода с клавиатуры на полях ввода.

3 288w

Теперь, пока форма не будет заполнена, кнопка будет заблокирована.

Что у нас получилось

Мы сделали форму, которая отправляет данные без перезагрузки страницы, показывает сообщение об ошибке или успешной отправке и блокирует кнопку, пока не введены значения.

4 300w

Для всего этого мы использовали методы HTML-элементов и элементов форм, которые нам предоставляет браузер и веб-платформа.

Конечно, этим работа с формами не заканчивается. Ещё можно сделать валидацию каждого поля в отдельности, загрузку картинок с возможностью их редактирования, добавить всякие комбо-боксы и нестандартные элементы.

Но для первой формы, с которой мы работали в JS, этого достаточно ?

Источник

Отправка данных формы

Предварительные знания: Базовая компьютерная грамотность, понимание HTML и базовые знания по HTTP и программированию на стороне сервера.
Задача: Понять, что происходит при отправке данных формы, в том числе получить представление о том, как данные обрабатываются на стороне сервера.

Куда отправляются данные?

Здесь мы обсудим, что происходит с данными при отправке формы.

О клиентской/серверной архитектуре

WEB основан на очень простой клиент-серверной архитектуре, которую можно обобщить следующим образом: клиент (обычно веб-браузер) отправляет запрос на сервер (в основном веб-сервер, такой как Apache, Nginx, IIS, Tomcat, и т. д.), используя протокол HTTP. Сервер отвечает на запрос, используя тот же протокол.client server

Примечание: Для получения более полного представления о том, как работают клиент-серверные архитектуры, ознакомьтесь с модулем «Первые шаги в программировании на стороне сервера».

На стороне клиента: определение способа отправки данных

Атрибут action

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

В этом примере данные отправляются на абсолютный URL — http://foo.com :

Если атрибуты не указаны, как показано ниже, данные из формы отправляются на ту же страницу, на которой размещается данная форма:

Многие старые страницы используют следующий синтаксис, чтобы указать, что данные должны быть отправлены на ту же страницу, которая содержит форму; это было необходимо, потому что до появления HTML5 атрибут action был обязательным. Это больше не нужно.

Атрибут method

Чтобы понять разницу между этими двумя методами, давайте вернёмся назад и рассмотрим, как работает HTTP. Каждый раз, когда вы хотите получить доступ к ресурсу в Интернете, браузер отправляет запрос на URL-адрес. HTTP-запрос состоит из двух частей: заголовка, который содержит набор глобальных метаданных о возможностях браузера, и тела, которое может содержать информацию, необходимую серверу для обработки конкретного запроса.

Метод GET

Рассмотрим следующую форму:

HTTP-запрос имеет следующий вид:

Примечание: вы можете найти этот пример на GitHub — смотрите get-method.html (see it live also).

Метод POST

Метод POST немного отличается. Браузер использует этот метод для связи с сервером при запросе ответа с учётом данных, представленные в теле HTTP-запроса: «Эй, сервер, взгляни на эти данные и отправь мне соответствующий результат». Если форма отправляется с использованием этого метода, данные добавляются в тело HTTP-запроса.

Заголовок Content-Length указывает размер тела, а заголовок Content-Type указывает тип данных, отправляемых на сервер. Мы обсудим эти заголовки позже.

Примечание: вы можете найти этот пример на GitHub — смотрите post-method.html (see it live also).

Просмотр HTTP-запросов

HTTP-запросы никогда не отображаются пользователю (если вы хотите их видеть, вам нужно использовать такие инструменты, как Firefox Network Monitor или Chrome Developer Tools). Например, данные формы можно увидеть на вкладке Сеть (Network) в Chrome следующим образом (после отправки формы):

Затем вы можете получить данные формы, как показано на рисунке ниже.

network monitor

Единственное, что отображается пользователю — вызываемый URL. Как упоминалось раннее, запрос с методом GET позволит пользователю увидеть информацию из запроса в URL, а запрос с методом POST не позволит. Две причины, почему это может быть важно:

На стороне сервера: получение данных

Какой бы HTTP вы не выбрали, сервер возвращает строку, которая будет последовательно проанализирована для получения данных в формате листа с парами ключ/значение. Способ получения доступа к этому листу зависит от платформы разработки или особенностей фреймворка, который вы можете использовать. Технологии, которые вы используете, определяют, как обрабатываются скопированные ключи. Часто, приоритетным является последнее полученное значение для данного ключа.

Пример: Чистый PHP

php result

Примечание: Этот пример не будет работать, когда вы загружаете его в браузер локально — браузер не может интерпретировать PHP код, после отправки данных из формы, браузер просто предложит загрузить PHP файл. Чтобы пример заработал, необходимо отправить его на PHP сервер. Для тестирования PHP на локальных серверах можете пробовать MAMP (Mac and Windows) и/или AMPPS (Mac, Windows, Linux).

Пример: Python

Этот пример показывает, как вы можете использовать Python для решения той же задачи — отобразить отправленные данные на странице. В этом примере используется Flask framework для визуализации шаблонов, поддерживающих форму отправки данных (смотри python-example.py).

Два шаблона из коде выше взаимодействуют так:

Другие языки и фреймворки

Примечание: Обучению фреймворкам и работе с серверами не входит в рамки этой статьи. Если хотите узнать больше, ссылки ниже помогут в этом.

Особый случай: отправка файлов

Отправка файлов с помощью форм HTML — это особый случай. Файлы — это бинарные данные или рассматриваются как таковые, в то время как все остальные — это текстовые данные. Поскольку HTTP — это текстовый протокол, есть особые требования для работы с бинарными данными.

Атрибут enctype

Если хотите отправить файл, нужно сделать следующие три шага:

Предупреждение: Многие сервера имеют заданные ограничения на размер загружаемых файлов и запросы от пользователей, чтобы защититься от возможных злоупотреблений. Важно проверять эти ограничения у администратора сервера, прежде чем загружать файлы.

Проблемы безопасности

Каждый раз, когда вы отправляете данные на сервер, вы должны учитывать безопасность. HTML-формы являются наиболее распространёнными векторами атак на серверы(места, где могут происходить атаки). Проблемы вытекают не из самих форм HTML, а из-за того, как сервер обрабатывает данные из этих форм.

В зависимости от того, что вы делаете, вы можете столкнуться с некоторыми очень известными проблемами безопасности:

XSS «Межсайтовый скриптинг» и CSRF «Подделка межсайтовых запросов»

Межсайтовый скриптинг (XSS «Cross Site Request Forgery») позволяет злоумышленникам внедрить клиентский скрипт в веб-страницы, просматриваемые другими пользователями. Подделка межсайтовых запросов (CSRF «Cross-Site Scripting») может использоваться злоумышленниками для обхода средств контроля доступа, таких как одна и та же политика происхождения. Последствие от этих атак может варьироваться от мелких неудобств до значительного риска безопасности.

Чтобы предотвратить эти атаки, вы всегда должны проверять данные, которые пользователь отправляет на ваш сервер, и (если вам нужно отобразить их) стараться не отображать HTML-контент, предоставленный пользователем. Вместо этого вы должны обработать предоставленные пользователем данные, чтобы не отображать их слово в слово. Сегодня почти все платформы на рынке реализуют минимальный «фильтр», который удаляет элементы HTML

Источник

Понравилась статья? Поделить с друзьями:
Добавить комментарий
  • Как сделать успешный бизнес на ритуальных услугах
  • Выездной кейтеринг в России
  • Риски бизнеса: без чего не обойтись на пути к успеху
  • js добавить input в форму
  • js валидация формы email