Задать вопрос
Ответы пользователя по тегу JavaScript
  • Как ограничить выход элемента за другой?

    0xD34F
    @0xD34F Куратор тега JavaScript
    новая_координата = Math.max(
      минимальное_допустимое_значение,
      Math.min(
        максимальное_допустимое_значение,
        текущая_координата + изменение_координаты
      )
    );

    https://jsfiddle.net/03mh1q78/
    Ответ написан
    Комментировать
  • Регулярное выражение для замены img на picture?

    0xD34F
    @0xD34F Куратор тега JavaScript
    Как получить src:

    const getSrc = img => img.getAttribute('src');
    // или
    const getSrc = img => img.attributes.src.value;

    Чтобы дальше два раза одно и то же не писать, небольшой декоратор:

    const relativeOnly = f => img => {
      const src = getSrc(img);
      if (!/^https?:\/\//.test(src)) {
        f(img, src);
      }
    };

    Можно перезаписывать разметку:

    document.querySelectorAll('img').forEach(relativeOnly((img, src) =>
      img.outerHTML = `
        <picture>
          <source srcset="${src}" type="image/svg+xml">
          ${img.outerHTML}
        </picture>`
    ));

    Или, создавать новые элементы напрямую:

    const wrapImages = relativeOnly((img, src) => {
      const picture = document.createElement('picture');
      const source = document.createElement('source');
      source.srcset = src;
      source.type = 'image/svg+xml';
      img.replaceWith(picture);
      picture.append(source, img);
    });

    for (const n of document.getElementsByTagName('img')) {
      wrapImages(n);
    }
    
    // или
    
    Array.prototype.forEach.call(document.images, wrapImages);
    Ответ написан
  • Как разделить значения массива через запятую и обернуть их в кавычки?

    0xD34F
    @0xD34F Куратор тега JavaScript
    О каком количестве значений идёт речь: const count = 10;.

    const result = Array
      .from({ length: count }, (n, i) => `"${-~i}"`)
      .join(', ');
    
    // или
    
    const result = [...Array(count).keys()].reduce((acc, n) => {
      return acc.concat(acc && ', ', '"', ++n, '"');
    }, '');
    
    // или
    
    const result = (function xxx(n) {
      return n > 0 ? xxx(n - 1) + (n === 1 ? '' : ', ') + '"' + n + '"' : '';
    })(count);
    
    // или
    
    const result = Array(count)
      .fill()
      .map((n, i) => JSON.stringify((i + 1).toString()))
      .toString()
      .replace(/,/g, '$& ');
    Ответ написан
    Комментировать
  • Как повесить события на динамический дочерний класс?

    0xD34F
    @0xD34F Куратор тега JavaScript
    Куда надо нажимать и какие классы переключать:

    const buttonSelector = '.class2';
    const classesToToggle = [ 'class3', 'class4' ];

    Переключаем:

    $(buttonSelector).click(e => {
      $(e.currentTarget).next().toggleClass(classesToToggle.join(' '));
    });

    или

    document.querySelectorAll(buttonSelector).forEach(n => {
      n.addEventListener('click', onClick);
    });
    
    function onClick() {
      classesToToggle.forEach(n => this.nextElementSibling.classList.toggle(n));
    }

    или

    document.addEventListener('click', e => {
      const button = e.target.closest(buttonSelector);
      if (button) {
        for (const n of classesToToggle) {
          button.parentNode.lastElementChild.classList.toggle(n);
        }
      }
    });
    Ответ написан
    2 комментария
  • Как сделать циклическую смену 3х цветов фона div при наведении и отведении мыши?

    0xD34F
    @0xD34F Куратор тега JavaScript
    const el = document.querySelector('#box');
    const colors = [ 'red', 'green', 'blue' ];
    let index = -1;
    
    el.addEventListener('mouseenter', function() {
      index = (index + 1) % colors.length;
      this.style.backgroundColor = colors[index];
    });
    el.addEventListener('mouseleave', function() {
      this.style.backgroundColor = '';
    });
    Ответ написан
    Комментировать
  • Как ограничить количество одновременных запросов на сервер?

    0xD34F
    @0xD34F Куратор тега JavaScript
    подскажите, что мне нужно подправить

    Всё нужно подправить. А ещё больше - сделать.

    Ваша функция никак не извещает вызывающий код о том, что запросы выполнены и с какими результатами; результаты запросов - вместо того, чтобы сохранять все, сохраняете только последний полученный; вместо того, чтобы ограничить количество одновременно выполняемых запросов вы пропускаете один (кажется, вы просто не понимаете, как работает forEach).

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

    Под хранение результатов запросов создадим массив.

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

    Также создадим два счётчика - количество отправленных запросов и количество полученных ответов.

    Собственно запрос. Первым делом проверяем, сколько ответов получено - если все, резолвим промис массивом результатов. В противном случае проверяем, сколько запросов отправлено - если не все, увеличиваем соответствующий счётчик, отправляем запрос; по получении ответа достаём массив индексов url'а, сохраняем результат, увеличиваем счётчик полученных ответов и пытаемся отправить следующий запрос.

    Ну и запускаем всё это дело в работу - обычный цикл for, в котором на каждой итерации выполняется запрос. Минимум одна итерация (это на тот случай, если массив url'ов пустой), максимум - количество уникальных url'ов или ограничение на количество одновременных запросов, меньшее из этих значений.

    Всё:

    function makeRequests(urls, max) {
      return new Promise(resolve => {
        const results = Array(urls.length).fill(null);
        const groupedUrls = urls.reduce((acc, n, i) => ((acc[n] ??= []).push(i), acc), {});
        const uniqueUrls = Object.keys(groupedUrls);
        let countRequests = 0;
        let countResponses = 0;
    
        for (let i = 0; i < Math.max(1, Math.min(max, uniqueUrls.length)); i++) {
          request();
        }
    
        function request() {
          if (countResponses === uniqueUrls.length) {
            resolve(results);
          } else if (countRequests < uniqueUrls.length) {
            const url = uniqueUrls[countRequests++];
            fetch(url)
              .then(result => result.json())
              .catch(error => error)
              .then(result => {
                groupedUrls[url].forEach(n => results[n] = result);
                countResponses++;
                request();
              });
          }
        }
      });
    }

    Ответ написан
    Комментировать
  • Почему закомментированный вариант не работает, как первый?

    0xD34F
    @0xD34F Куратор тега JavaScript
    Комментировать
  • Почему данный код работает не правильно?

    0xD34F
    @0xD34F Куратор тега JavaScript
    Причина номер раз: потому что автор кода говнокодер. Ну, то есть - вы даже отформатировать код не удосужились. Причина номер два: проверяется состояние только одного инпута, того, где случилось событие. А надо проверять все.

    Исправляем:

    $('form').on('input', function() {
      $('.button').prop('disabled', $('input', this).get().some(n => !n.value));
    }).trigger('input');
    
    // или
    
    const form = document.querySelector('form');
    const button = document.querySelector('.button');
    const inputs = [...form.querySelectorAll('input')];
    form.addEventListener('input', () => button.disabled = !inputs.every(n => n.value));
    form.dispatchEvent(new Event('input'));
    Ответ написан
    5 комментариев
  • Как получать случайные уникальные числа из заданного интервала?

    0xD34F
    @0xD34F Куратор тега JavaScript
    Создать массив, выдёргивать случайный элемент:

    function makeRandomizer([ min, max ]) {
      const numbers = [...Array(max - min + 1).keys()];
    
      return () => numbers.length
        ? min + numbers.splice(Math.random() * numbers.length | 0, 1)[0]
        : null;
    }

    Создать массив, перемешать его, доставать последний элемент:

    function makeRandomizer([ min, max ]) {
      const numbers = Array.from({ length: max - min + 1 }, (n, i) => min + i);
    
      for (let i = numbers.length; --i > 0;) {
        const j = Math.random() * (i + 1) | 0;
        [ numbers[i], numbers[j] ] = [ numbers[j], numbers[i] ];
      }
    
      return () => numbers.pop() ?? null;
    }
    Ответ написан
    5 комментариев
  • Как определить selected option?

    0xD34F
    @0xD34F Куратор тега JavaScript
    $('#sel1').change(function() {
      const min = +$(this).val();
    
      $('#sel2')
        .val((i, v) => Math.max(v, min))
        .children()
        .show()
        .filter((i, n) => +n.value < min)
        .hide();
    }).change();

    или

    const select1 = document.querySelector('#sel1');
    const select2 = document.querySelector('#sel2');
    
    select1.addEventListener('change', e => {
      const min = +e.target.value;
      select2.value = Math.max(select2.value, min);
      for (const n of select2.children) {
        n.hidden = +n.value < min;
      }
    });
    
    select1.dispatchEvent(new Event('change'));
    Ответ написан
  • Возможно ли посчитать количеств элементов в одном ряду?

    0xD34F
    @0xD34F Куратор тега JavaScript
    const count = Array.prototype.reduce.call(
      document.querySelectorAll('.col'),
      (acc, n, i, a) => (
        n.offsetTop !== a[i - 1]?.offsetTop && acc.push(0),
        acc[acc.length - 1]++,
        acc
      ),
      []
    );
    Ответ написан
    Комментировать
  • Как удалять предыдущий DOM элемент при появлении нового элемента контейнера?

    0xD34F
    @0xD34F Куратор тега JavaScript
    Вместо filterInput.addEventListener('keyup',... пусть будет

    filterInput.addEventListener('input', function() {
      const value = this.value.toLowerCase();
    
      filterResult.innerHTML = resultArr
        .filter(n => n.toLowerCase().includes(value))
        .map(n => `<li>${n}</li>`)
        .join('');
    });

    или

    filterInput.addEventListener('input', e => {
      const value = e.target.value.toLowerCase();
    
      filterResult.replaceChildren(...resultArr.reduce((acc, n) => {
        if (n.toLowerCase().indexOf(value) !== -1) {
          (acc[acc.length] = document.createElement('li')).textContent = n;
        }
        return acc;
      }, []));
    });
    Ответ написан
    2 комментария
  • Как узнать, есть ли в массивах одинаковые значения?

    0xD34F
    @0xD34F Куратор тега JavaScript
    Делаем просто, ровно то, что спрошено в вопросе:

    arr1.filter(n => arr2.includes(n)).length !== 0
    
    // или
    
    arr1.some(Set.prototype.has.bind(new Set(arr2)))
    
    // или
    
    new Set(arr1).size + new Set(arr2).size > new Set([ ...arr1, ...arr2 ]).size

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

    function intersection(data1, data2, key = n => n) {
      const getKey = key instanceof Function ? key : n => n[key];
      const keys = new Set(Array.from(data2, getKey));
      const result = [];
    
      for (const n of data1) {
        if (keys.has(getKey(n))) {
          result.push(n);
        }
      }
    
      return result;
    }
    
    
    // как применять в вашем случае
    !!intersection(arr1, arr2).length
    
    // другие примеры использования
    intersection(Array(5).keys(), Array(3).keys()) // [0, 1, 2]
    intersection('abcDe', 'cd', n => n.toLowerCase()) // ['c', 'D']
    intersection([{id: 1}, {id: 2}, {id: 3}], [{id: 2}, {id: 4}], 'id') // [{id: 2}]
    Ответ написан
    1 комментарий
  • Как сделать заблокированный button если checkbox пустой?

    0xD34F
    @0xD34F Куратор тега JavaScript
    Сначала пара замечаний:
    1. В select'е первому option'у добавьте атрибут value="". Соответственно, проверять надо будет value на пустоту, а не равенство тексту. Это и проще, и код не придётся переписывать, если вдруг завтра вам потребуется изменить текст по умолчанию.
    2. Не забывайте про всплытие событий. Обработчик события input можно добавлять один раз - общему предку (у вас это .element), а не каждому элементу индивидуально. Не придётся вписывать новый обработчик или удалять существующий, если изменится количество элементов в форме.


    Ну и собственно блокировка кнопки:

    button.disabled = ![
      inputMail.value,
      inputPhone.value,
      select.value,
      checkbox.checked,
    ].every(Boolean);

    https://jsfiddle.net/rmduyegf/
    Ответ написан
    Комментировать
  • Как вытащить из строки слово, заключенное в скобки?

    0xD34F
    @0xD34F Куратор тега JavaScript
    str.match(/(?<=\{{2}).*?(?=\}{2})/g) ?? []
    
    // или
    
    Array.from(str.matchAll(/\{\{(.*?)\}\}/g), n => n[1])
    Ответ написан
    3 комментария
  • Как с помощью js удалить слова item и items?

    0xD34F
    @0xD34F Куратор тега JavaScript
    Где удалять: const className = 'cart__counter';.

    Как удалять:

    const remove = el => el.innerText = parseInt(el.innerText);
    // или
    const remove = el => el.innerHTML = el.innerHTML.match(/\d+/)[0];
    // или
    const remove = el => el.textContent = el.textContent.replace(/\D/g, '');
    // или
    const remove = el => [ el.firstChild.data ] = el.firstChild.data.split(' ');
    // или
    const remove = el => el.childNodes[0].nodeValue = parseFloat(el.childNodes[0].nodeValue);

    Удаляем:

    document.querySelectorAll(`.${className}`).forEach(remove);
    
    // или
    
    for (const n of document.getElementsByClassName(className)) {
      remove(n);
    }

    Но вообще, это конечно костыль. Вы бы посмотрели - может есть возможность настроить корзину так, чтобы лишние надписи изначально не выводились.
    Ответ написан
    1 комментарий
  • Можно лучше сделать массив из сортированных данных объекта?

    0xD34F
    @0xD34F Куратор тега JavaScript
    Сортируем:

    const arr = Object
      .entries(obj)
      .sort((a, b) => b[1] - a[1])
      .map(n => n[0]);

    Или не сортируем, но всё равно получаем отсортированные данные (не надо так делать, работает только потому, что значения целые неотрицательные):

    const arr = Object
      .values(Object.entries(obj).reduce((acc, n) => ((acc[n[1]] ??= []).push(n[0]), acc), {}))
      .reverse()
      .flat();
    Ответ написан
    1 комментарий
  • Как оптимизировать удаление параметров из url query string?

    0xD34F
    @0xD34F Куратор тега JavaScript
    [...params.keys()].forEach(n => whiteList.includes(n) || params.delete(n));
    Ответ написан
    6 комментариев
  • Как рекурсивно удалить текстовые узлы?

    0xD34F
    @0xD34F Куратор тега JavaScript
    Во-первых - вы передаёте в функцию строку и используете её в качестве селектора. Как быть с элементами, которые селектору не соответствуют? Они не будут найдены. Пусть функция сразу получает узел DOM-дерева.

    Во-вторых - вы обходите childNodes, который представляет собой динамический NodeList и одновременно пытаетесь его модифицировать. Удаляя один узел, вы пропускаете следующий - т.е., если несколько текстовых узлов идут подряд, то удалён будет только каждый второй; а те нетекстовые узлы, которые расположены после текстовых, тут никакой рекурсии не случится, их содержимое вообще никак не будет обработано. Надо делать копию childNodes, и перебирать её. Или вместо for...of использовать цикл со счётчиком, при удалении узла счётчик увеличиваться не должен. Или перебирать childNodes от конца к началу - тоже цикл со счётчиком или reduceRight (да, это будет использование метода не совсем по назначению).

    В-третьих - какой-то бред с nextElementSibling, не знаю, как это комментировать. Надо было просто вызвать функцию, передав ей текущий элемент.

    В четвёртых:

    const deleteTextNodes = node =>
      node.nodeType === Node.TEXT_NODE
        ? node.remove()
        : [...node.childNodes].forEach(deleteTextNodes);
    
    // или
    
    function deleteTextNodes(node) {
      if (node instanceof Text) {
        node.remove();
      } else {
        Array.prototype.reduceRight.call(node.childNodes, (_, n) => deleteTextNodes(n), null);
      }
    }
    Ответ написан