Задать вопрос
Ответы пользователя по тегу JavaScript
  • JS Promise Memoize как правильно описать функцию?

    0xD34F
    @0xD34F Куратор тега JavaScript
    function memoize(f, timeout) {
      const memo = {};
    
      return () => (new Date() < memo.time + timeout)
        ? Promise.resolve(memo.result)
        : f().then(r => (memo.time = +new Date(), memo.result = r));
    }
    Ответ написан
    Комментировать
  • Как отсортировать DOM элементы в обратную сторону на чистом js?

    0xD34F
    @0xD34F Куратор тега JavaScript
    Где надо развернуть элементы: const parent = document.querySelector('ul');.

    Разворачиваем:

    parent.querySelectorAll(':scope > *').forEach(n => parent.prepend(n));
    
    // или
    
    Element.prototype.append.apply(parent, [...parent.children].reverse());
    
    // или
    
    const [ first, ...rest ] = parent.children;
    first?.before(...rest.reverse());
    
    // или
    
    for (const n of parent.children) {
      parent.insertBefore(n, parent.firstElementChild);
    }
    
    // или
    
    for (let i = parent.children.length; i--;) {
      parent.insertAdjacentElement('beforeend', parent.children[i]);
    }
    
    // или
    
    const elems = Array.from(parent.children);
    while (elems.length) {
      parent.appendChild(elems.pop());
    }
    Ответ написан
    7 комментариев
  • Как определить первые цифры чисел массива?

    0xD34F
    @0xD34F Куратор тега JavaScript
    Если исправлять/дополнять существующий код, то, во-первых, определять первую цифру надо для каждого из элементов массива отдельно, т.е. let first = вам следовало разместить внутри цикла, а не перед ним; во-вторых, проще всего получить цифру старшего разряда можно превратив число в строку и взяв нулевой символ. Т.е., вы пытались изобразить нечто подобное:

    for (const n of arr) {
      const [ first ] = String(n);
      if (first == 1 || first == 2 || first == 5) {
        console.log(n);
      }
    }

    Но вообще, можно сделать и иначе.

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

    // ...строкой
    const first = '125';
    const startsWithFirst = n => !!~first.indexOf(`${n}`[0]);
    
    // ...массивом чисел
    const first = [ 1, 2, 5 ];
    const startsWithFirst = n => first.includes(n / (10 ** (Math.log10(n) | 0)) | 0);
    
    // ...числом
    const first = 0b100110;
    const startsWithFirst = n => ((first >> ('' + n).charAt()) & 1) === 1;
    
    // ...регулярным выражением
    const first = /^[125]/;
    const startsWithFirst = n => first.test(n);

    Кроме того, цикл в явном виде не нужен, собираем и выводим подходящие числа:

    // все сразу
    console.log(arr.filter(startsWithFirst));
    
    // или по одному
    arr.forEach(n => startsWithFirst(n) && console.log(n));
    Ответ написан
    Комментировать
  • Как ограничить выход элемента за другой?

    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('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;
      }, []));
    });

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

    filterInput.addEventListener('input', () => {
      const value = filterInput.value.toLowerCase();
      const r = resultArr.filter(n => ~-n.toLowerCase().split(value).length);
      const c = filterResult.children;
    
      for (; c.length > r.length; filterResult.lastElementChild.remove()) ;
      for (; c.length < r.length; filterResult.append(document.createElement('li'))) ;
    
      r.forEach((n, i) => c[i].innerText = n);
    });
    Ответ написан
    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 комментарий