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

    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 },
        (_, i) => min + i
      );
    
      for (let i = numbers.length; --i > 0;) {
        const j = Math.floor(Math.random() * (i + 1));
        [ 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 комментарий
  • Можно лучше сделать массив из сортированных данных объекта?

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

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

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

    const arr = Object
      .values(Object.keys(obj).reduce((acc, n) => ((acc[obj[n]] ??= []).push(n), 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.replaceWith();
      } else {
        for (let i = node.childNodes.length; i--;) {
          deleteTextNodes(node.childNodes[i]);
        }
      }
    }
    Ответ написан
  • Как найти URL среди списка URL, если могут присутствовать динамические параметры?

    0xD34F
    @0xD34F Куратор тега JavaScript
    urlList.findIndex(n => RegExp(`^${n.replace(/:\w+/g, '\\w+')}$`).test(url))
    Ответ написан
    Комментировать
  • Как сделать выборку по шансу?

    0xD34F
    @0xD34F Куратор тега JavaScript
    if (total_sum >= rand) {

    Равенство лишнее, верхняя граница не должна учитываться. Из-за этого у вас перекос в результатах.

    Ну и конечно можно всё это записать гораздо короче:

    function getRandom(arr, key) {
      const rand = Math.random() * arr.reduce((acc, n) => acc + n[key], 0);
      let sum = 0;
      return arr.find(n => (sum += n[key]) > rand);
    }
    
    
    const obj = getRandom(data, 'weight');
    Ответ написан
    Комментировать
  • Как при клике на span вставлять его содержимое в инпут?

    0xD34F
    @0xD34F Куратор тега JavaScript
    Перебираем предыдущие элементы относительно кликнутого, пока не встретится подходящий:

    document.addEventListener('click', ({ target: t }) => {
      if (t.matches('.oldValue')) {
        let input = t;
        while (!(input = input.previousElementSibling).matches('.value')) ;
        input.value = t.textContent;
      }
    });

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

    const inputs = document.querySelectorAll('.value');
    const spans = [...document.querySelectorAll('.oldValue')];
    const onClick = ({ target: t }) => inputs[spans.indexOf(t)].value = t.innerText;
    spans.forEach(n => n.addEventListener('click', onClick));

    Или, добавьте общие обёртки каждой паре input-span, и тогда можно будет подниматься до этой обёртки и искать нужный элемент внутри неё:

    document.addEventListener('click', ({ target: t }) => {
      if (t.classList.contains('oldValue')) {
        t.closest('селектор общей обёртки').querySelector('.value').value = t.innerHTML;
      }
    });
    Ответ написан
  • Почему появляются запятые после отработки цикла?

    0xD34F
    @0xD34F Куратор тега JavaScript
    Потому что при преобразовании массива в строку, согласно спецификации, используется метод join. Который по умолчанию в качестве разделителя (опять же, согласно спецификации) использует запятую.

    "Как убрать" - очевидно, вызывать join самостоятельно, указывая пустую строку:

    Array.from(data.anchor, n => `<a href="${n.href}">${n.value}</a>`).join('')

    Или, есть вариант вместо join объединять строки с помощью concat:

    ''.concat(...[...data.anchor].map(n => `<a href="${n.href}">${n.value}</a>`))

    Ещё можно (но не нужно) использовать оператор +:

    Array.prototype.reduce.call(
      data.anchor,
      (acc, n) => acc + `<a href="${n.href}">${n.value}</a>`,
      ''
    )
    Ответ написан
    Комментировать
  • Как обрабатывать клик на одном элементе только после клика по другому?

    0xD34F
    @0xD34F Куратор тега JavaScript
    let isBtn1Clicked = false;
    
    button1.addEventListener('click', () => isBtn1Clicked = true);
    button2.addEventListener('click', () => {
      if (isBtn1Clicked) {
        // ...
      }
    });

    или

    button1.addEventListener('click', e => e.target.classList.add('clicked'));
    button2.addEventListener('click', () => {
      if (button1.classList.contains('clicked')) {
        // ...
      }
    });

    или

    button2.disabled = true;
    
    button1.addEventListener('click', () => button2.disabled = false);
    button2.addEventListener('click', () => {
      // ...
    });

    или

    button2.hidden = true;
    
    button1.addEventListener('click', () => button2.hidden = false);
    button2.addEventListener('click', () => {
      // ...
    });

    или

    button1.addEventListener('click', () => {
      button2.addEventListener('click', () => {
        // ...
      });
    }, { once: true });
    Ответ написан
    1 комментарий
  • JS. Как из массива объектов сделать объект с параметрами из свойств?

    0xD34F
    @0xD34F Куратор тега JavaScript
    const obj = Object.fromEntries(arr.map(n => [ n.name, n.number ]));
    // или
    const obj = arr.reduce((acc, n) => (acc[n.name] = n.number, acc), {});
    // или
    const obj = Object.assign({}, ...arr.map(n => ({ [n.name]: n.number })));

    Или, в более общем виде:

    function toObj(data, key, val = n => n) {
      const getKey = key instanceof Function ? key : n => n[key];
      const getVal = val instanceof Function ? val : n => n[val];
      const obj = {};
    
      for (const n of data) {
        obj[getKey(n)] = getVal(n);
      }
    
      return obj;
    }

    Пользоваться этим можно так (ваш случай):

    const obj = toObj(arr, 'name', 'number');
    // {Kolya: '5', Olga: '10'}

    А можно так:

    const charCodes = toObj('abc', n => n.charCodeAt());
    // {97: 'a', 98: 'b', 99: 'c'}

    И даже так тоже можно:

    <input name="xxx" value="69">
    <input name="yyy" value="187">
    <input name="zzz" value="666">

    const inputValues = toObj(document.querySelectorAll('input'), 'name', 'value');
    // {xxx: '69', yyy: '187', zzz: '666'}
    Ответ написан
    3 комментария
  • Как вывести сумму значений 2 select?

    0xD34F
    @0xD34F Куратор тега JavaScript
    Собираем объект вида { город: стоимость_доставки }:

    const prices = {
      'Москва': 100,
      'Санкт-Петербург': 200,
      'Казань': 300,
    };

    Как посчитать стоимость доставки:

    const getPrice = (city1, city2) =>
      city1 === city2
        ? prices[city1]
        : prices[city1] + prices[city2];

    По событию change у формы достаём значения select'ов, считаем доставку:

    $('form').on('change', function() {
      const [ city1, city2 ] = $('select', this).get().map(n => n.value);
      $('input', this).val(getPrice(city1, city2));
    }).change();

    или

    const form = document.querySelector('form');
    form.addEventListener('change', function() {
      const cities = Array.from(this.querySelectorAll('select'), n => n.value);
      this.querySelector('input').value = getPrice(...cities);
    });
    form.dispatchEvent(new Event('change'));
    Ответ написан
    Комментировать
  • Как сделать последовательную анимацию блоков на чистом Js?

    0xD34F
    @0xD34F Куратор тега JavaScript
    button.addEventListener('click', async () => {
      for (const el of cards) {
        await el.animate([
          { transform: 'rotateX(0deg)' },
          { transform: 'rotateX(180deg)' },
        ], {
          duration: 1000,
          easing: 'linear',
          fill: 'forwards',
        }).finished;
      }
    });
    Ответ написан
    6 комментариев