кому не лень, поделитесь опытом по javascript

Решил я вот в одной из своих программ избавиться от jquery
Его там реально 4 строчки и нет смысла его тянуть.
Но что то я туплю.
У меня был некий обработчик события change у формы
$(document).on('change', this.config.filterWrapper, function () {
const filterData = $(this).serializeArray();
}
где this.config.filterWrapper это идентификатор формы.
Работает как нужно — события изменения любого элемента этой формы запускают этот код и что важно — this всегда ссылается на форму. Что позволяет следующей строкой получить в переменную все значения всех элементов формы, неважно какой именно был изменен.

Начинаю разбираться с событиями в javascript.
Опираюсь на это learn.javascript.ru/introduction-browser-events
и там описывается три способа создать реакцию на событие.
Есть три способа назначения обработчиков событий:
Атрибут HTML: onclick="...".
DOM-свойство: elem.onclick = function.
Специальные методы: elem.addEventListener(event, handler[, phase]) для добавления, removeEventListener для удаления.
Ну работать с инлайновым js и вписывать его в html не солидно, путаюсь понять работу addEventListener.
Пишу вот такое
document.addEventListener('change', function (event) {
            console.log(event.target);
        })
смущает тот факт, что отслеживаются все события change возникающие в документе (подозреваю что jquery работает под капотом так же просто это скрыто от нас). Но проблема на лицо — как отслеживать события только внутри определенной формы? На странице может быть их много. Приходит в голову мысль, что раз у нас есть объект event то через него можно проверять кто инициатор, кто target и уже по условию проверять наш ли это пациент. Но ожидаемо, что targеt это не есть форма, это dom элемент, на котором произошло событие — input, select и так далее. А значит мне по прежнему ничего не известно о том, из какой конкретно формы этот элемент. Да можно пытаться у каждого элемента проверять кто его предок, проверять у предка id и смотреть не наша ли это форма, но это плохо и плюс формы на всегда содержит только тег form и сразу inputы, там может быть сложная разметка div, span что угодно.

Так как же правильно обработать событие change чтобы в идеале event.target указывавал на форму в которой произошло измеенние, а не на сам элемент или хотя бы, а как грамотные люди определяют в какой форме на странице произошло изменение?
Александр Мельник
30 мая 2020, 17:53
modx.pro
1 042
0

Комментарии: 16

Александр Мельник
30 мая 2020, 18:23
0
а я дурак. наверное же вместо document addEventListener можно назначить на заранее полученный элемент.
    Александр Мельник
    30 мая 2020, 19:31
    0
    что-то переделка всего 4 строк с jquery на синтаксис javascript дается тяжко)
    А что нет никакого метода для получения данных из формы?
    .serializeArray();
    давал мне массив объектов в виде name — value
    0: Object { name: "price", value: "22000" }
    1: Object { name: "price", value: "150000" }
    2: Object { name: "brand", value: "Maugli" }
    length: 3
    а javascript не подскажите, имеет метод получения сразу всех заполненных полей формы?
    Я вот для себя открыл, что оказывается существует некая коллекция форм
    learn.javascript.ru/form-elements
    которая содержит в себе все формы, все элементы всех форм и так далее
    Могу делать цикл, сам создавать сам объекты и наполнить ими какой-то массив. Но тогда нужно еще и проверять какой элемент выбран (например если есть input type radio), ну тоесть я то нагорожу 30 строчек кода). Но может я просто не нашел удобного метода в языке js для работы с данными с формы?
      Сергей Шлоков
      30 мая 2020, 20:42
      +2
      Ну в 4 строчки на ванильном js ты не впишешься. Получится чуть больше, но зато тянуть всю jQuery ради 4 строчек не нужно.

      А что нет никакого метода для получения данных из формы?
      FormData в помощь.

      Может даже лучше тут.
        Александр Мельник
        30 мая 2020, 21:14
        +1
        Спасибо, но formdata он вроде для отправки форм. я смотрел на него, не подходит (или я не умею его готовить)
        Очень удобным для меня открытием оказались коллекции данных js — form
        learn.javascript.ru/form-elements
        сделал пока некий метод на них, но не уверен что смог учесть все нюансы. В форме могут содержаться элементы разных типов. Так например у инпутов нужно получать value, у textarea получать innerHtml, checkbox нужно проверять на наличие атрибута checked и так далее и это чтобы получить то, что мне давала даже не одна строка, а одно слово в jquery — serializeArray()
        Александр Мельник
        30 мая 2020, 21:35
        0
        а насчет fetch, да штука классная. Но это уже обработка запросов.
        А у меня другое. Я наверное запутал всех, акцентируя внимание на слове — форма.
        Это некий фильтр и в момент изменения любого его поля нужно собрать все текущие данные в виде массива объектов. Теоретически это может быть и вовсе не форма, а какие-то дивы. Ведь форма никогда и не отправляется по сути. Отслеживается событие change, все данные (выбранные чекбоксы например) собираются и далее уже пошла логика работы с этими данными. Форма здесь скорее дань традициям.
        Артем
        30 мая 2020, 22:18
        +2
        Хорошая практика — вешать обработчик на document, а затем разруливать его таргет через обычный if.

        document.addEventListener('change', e => {
            const target = e.target;
        
            if (target.matches('.some-selector')) { /* code */}
            // или можно прям на наличие класса проверять
            if (target.classList.contains('имя класса без точки')) { /* code */ }
        });

        Если тебе нужно отслеживать изменения внутри какой-то конкретной формы, то просто проверяешь ее наличие среди родителей через closest.
        if (target.closest('.form') !== null) {}

        Данные из формы удобнее всего собирать через FormData, как уже выше сказали. Иногда может потребоваться дополнительная обработка всяких чекбоксов, потому что по умолчанию неотмеченные чекбоксы вообще не собираются, например.
        К слову, FormData — итерируемый объект, поэтому ты можешь легко по нему пройтись через цикл и поменять то, что тебе нужно:
        for (const [key, value] of formDataInstance) {
            console.log(key, value);
        }

        fetch лично мне не зашел, слишком много телодвижений требуется для обычных ajax-запросов. Чтобы принять json на сервере, нужно лезть в php://input и оттуда его доставать, что может быть не всегда удобно по разным причинам. Тут либо написать свой простенький асинхронный метод-обертку для запросов, который будет построен на старом XMLHttpRequest, либо посылать данные в multipart/form-data, тогда проблем не будет.
        Помимо этого, fetch до сих пор не умеет отслеживать прогресс загрузки, поэтому если нужна такая фича, можно смело от него отказываться.
          Александр Мельник
          31 мая 2020, 08:53
          0
          Хорошая практика — вешать обработчик на document, а затем разруливать его таргет через обычный if.
          ну да, я вначале так и пробовал,
          Приходит в голову мысль, что раз у нас есть объект event то через него можно проверять кто инициатор, кто target и уже по условию проверять наш ли это пациент.
          но для меня проблемой стало то, что таргет как ему и положено всегда указывает на инициатора события, тоесть на input например, если мы изменили текст в нем. Но как определить, что этот инпут именно из той формы, за которой мы хотим следить?
          К примеру форма имеет атрибут id = «filter», но на странице есть еще 20 форм, формы заказа обратного звонка, калькулятора какого-то, еще что то.
          если наш листенер повешен на весь объект документ, то он будет следить за всеми элементами всех форм и единственный способ решить задачу, который я вижу, это всем input задавать определенный класс и проверять на его наличие.
          Пока сделал так
          let form = document.getElementById('formtest');
                  form.addEventListener('change', function (event) {}
          по крайней мере под мою задачу так оказалось удобнее. теперь событие возникает только если изменена конкретная форма и что важно для меня — target тоже всегда сама форма, а нее ее отдельный инпут.
          Насчет FormData нужно будет присмотреться. Я использовал ее несколько раз, но когда нужно было подготовить данные для отправки ajax-ом (хотя это можно сделать и вручную если заранее знаешь какие в форме input и их названия), я чаще пользовался formData чтобы передать через ajax файл. Но наверное вы и Сергей правы — собирать данные из формы эта штука должна уметь.
            Артем
            31 мая 2020, 15:04
            0
            Но как определить, что этот инпут именно из той формы, за которой мы хотим следить?
            Опять же, через closest.
            Он поднимается вверх по DOM и ищет указанного родителя.
            У тебя может быть хоть 150 одинаковых форм, но измененный инпут принадлежит только одной из них.
            Поэтому ты делаешь, например, вот так
            if (target.closest('.formClass') !== null) {}
            и узнаешь, что измененный инпут принадлежит форме с классом formClass.
            Более того, ты можешь сразу сохранить эту форму в переменную и в дальнейшем работать с ней, если есть такая необходимость.
            const form = target.closest('.formClass');
            if (form !== null) {}
            если наш листенер повешен на весь объект документ, то он будет следить за всеми элементами всех форм и единственный способ решить задачу, который я вижу, это всем input задавать определенный класс и проверять на его наличие.
            Верно, он будет следить за всеми элементами всех форм, в этом и есть его плюс, который называется делегирование.
            Например, если у тебя на странице появятся еще 2 формы с таким же классом, то они автоматически подхватятся и в них тоже будут отслеживаться изменения.
            И нет, не нужно задавать всем инпутам определенный класс.

            Твоя задача
            let form = document.getElementById('formtest');
             form.addEventListener('change', function (event) {}
            решается вот так
            document.addEventListener('change', e => {
                const target = e.target;
                if (target.closest('#formtest') !== null) {}
            });
            Это будет точно так же срабатывать, если изменения происходят только в рамках конкретной формы.
            Твоя форма может быть присвоена переменной, как я уже показал выше, чтобы ты мог с ней работать и собирать из нее данные через FormData, например.

            К слову, getElementById('formtest') уже практически не используется в новом коде, поэтому рекомендую заменять его на querySelector('#formtest').
              Aleksandr Huz
              01 июня 2020, 10:06
              +1
              К слову, getElementById('formtest') уже практически не используется в новом коде, поэтому рекомендую заменять его на querySelector('#formtest').
              А почему не используется? getElementById быстрее находит элемент, чем querySelector
                Артем
                01 июня 2020, 15:19
                +1
                querySelector настолько быстрый, что нет никакого смысла юзать getElementById ради скорости. В реальных сценариях в 99.9% случаев между ними не будет никакой разницы, потому что ощутить ее можно только при десятках тысяч операций. Более того, querySelector(All) — универсальный, поэтому можно ограничиться только им для всех сценариев, что в целом сделает твой код чуточку читабельнее.
            Александр Мельник
            31 мая 2020, 13:00
            0
            Артем, а подскажите, откуда вы брали информацию о методах у target? Да и на сколько я понимаю, это применимо к любому dom-элементу.
            matches
            classList
            closest
            Просто это очень похоже на методы jquery а вот у ванильного js я нигде не находил их описания.
            максимум
            learn.javascript.ru/dom-navigation
            но там эти методы не перечислены.
              Артем
              31 мая 2020, 15:08
              +1
              Например, вот тут они достаточно хорошо описаны.
              Практически всю нужную тебе инфу можно смотреть в рамках этого сайта. Просто пишешь прямо там в поиске нужный метод или фразу, а потом смотришь результаты.
            Дмитрий Иванов
            31 мая 2020, 13:17
            0
            А какой смысл отказа? Завтра еще что-то понадобится дописать, и опять писать кучу кода. А jq на каждом втором сайте, и он скорее всего уже у юзера в кэше лежит, если юзать ссылку на публичный cdn.
              Александр Мельник
              31 мая 2020, 13:24
              0
              В целом я с вами согласен. То что я пишу сейчас — это не кому-то на заказ, а просто для саморазвития, поэтому творю что хочу)
              Я когда начинаю что то разрабатывать совсем с нуля, иду от простого к сложного, сначала делаю наброски, пишу так как легче мне и проверяю саму идею. Если уже заработало и идея жизнеспособна, начинаю как-то код улучшать. Поэтому наброски как правило (если код использует js ) делаю на jquery.
              А на днях посмотрел, ну реально же всего 4 места где используется jquery (а тут еще месяцок назад на этом сайте под одним постом была долгая дискуссия, мол что js сейчас сам по себе идеален для работы c DOM) и стало интересно смогу или нет избавиться от него, просто для прокачки навыков скорее всего.
              Авторизуйтесь или зарегистрируйтесь, чтобы оставлять комментарии.
              16