Задать вопрос
Ответы пользователя по тегу JavaScript
  • Как обернуть каждый элемент в ссылку с помощью JS?

    0xD34F
    @0xD34F Куратор тега JavaScript
    document.querySelectorAll('.slider__itm img').forEach(n => {
      const link = document.createElement('a');
      n.parentNode.append(link);
      link.append(n);
    });

    или

    for (const n of document.querySelectorAll('.slider__itm')) {
      n.innerHTML = `<a>${n.innerHTML}</a>`;
    }

    или

    for (const n of document.getElementsByClassName('slider__itm')) {
      const link = document.createElement('a');
      link.appendChild(n.replaceChild(link, n.children[0]));
    }

    или

    const imgs = document.querySelectorAll('.slider__itm img');
    
    for (let i = 0; i < imgs.length; i++) {
      const link = document.createElement('a');
      imgs[i].replaceWith(link);
      link.insertAdjacentElement('afterbegin', imgs[i]);
    }
    Ответ написан
    3 комментария
  • Как отсортировать сегменты маршрута в порядке их прохождения?

    0xD34F
    @0xD34F Куратор тега JavaScript
    Собираем начальные и конечные точки сегментов маршрута (объекты вида { точка: сегмент }). Находим начальный сегмент маршрута - такой, начальная точка которого не является ничьей конечной. Следующий сегмент маршрута - такой, начальная точка которого является конечной точкой текущего сегмента. Ну и крутим цикл до тех пор, пока текущий сегмент маршрута существует, не забывая сохранять его в результирующий массив:

    function sort(route) {
      const pointsFrom = Object.fromEntries(route.map(n => [ n.from, n ]));
      const pointsTo = Object.fromEntries(route.map(n => [ n.to, n ]));
      const sorted = [];
    
      for (
        let segment = route.find(n => !pointsTo[n.from]);
        segment;
        segment = pointsFrom[segment.to]
      ) {
        sorted.push(segment);
      }
    
      return sorted;
    }
    Ответ написан
    Комментировать
  • Как удалить год из html разметки через JS?

    0xD34F
    @0xD34F Куратор тега JavaScript
    const remove = str => str.split(' ').slice(0, -1).join(' ');
    // или
    const remove = str => str.replace(/\s\S+$/, '');
    // или
    const remove = str => str.match(/.+(?=\s)/);
    // или
    const remove = str => str.slice(0, str.lastIndexOf(' '));

    for (const n of document.getElementsByClassName('startdate')) {
      n.textContent = remove(n.textContent);
    }
    
    // или, раз уж полной даты уже не будет, пусть начало и конец интервала дат
    // располагаются внутри одного элемента - вместе с куском строки также
    // удаляем и родительский элемент
    
    document.querySelectorAll('.startdate').forEach(n => {
      n.outerHTML = remove(n.innerHTML);
      // или
      n.replaceWith(remove(n.innerText));
    });
    Ответ написан
    Комментировать
  • Как удалить объект в многомерном массиве на основе значения ключа?

    0xD34F
    @0xD34F Куратор тега JavaScript
    Функция принимает массив и функцию (типа как встроенные методы массива - map, filter, some и т.д.), принимающую элемент массива и возвращающую true в случае, если элемент должен быть удалён. Если элемент удалять не надо, то следует обработать массив вложенных элементов - выполняется рекурсивный вызов. Как это может выглядеть:

    function deleteNested(arr, fn) {
      arr?.reduceRight?.((_, n, i, a) =>
        fn(n, i, a)
          ? a.splice(i, 1)
          : deleteNested(n.children, fn)
      , null);
    }

    или

    function deleteNested(arr, fn) {
      if (Array.isArray(arr)) {
        arr.splice(0, arr.length, ...arr.filter((n, i, a) => {
          return !fn(n, i, a) && (deleteNested(n.children, fn), true);
        }));
      }
    }

    или

    function deleteNested(arr, fn) {
      if (arr instanceof Array) {
        let numDeleted = 0;
    
        for (const [ i, n ] of arr.entries()) {
          if (fn(n, i, arr)) {
            numDeleted++;
          } else {
            arr[i - numDeleted] = n;
            deleteNested(n.children, fn);
          }
        }
    
        arr.length -= numDeleted;
      }
    }

    Использовать так:

    deleteNested(itemsData, n => n.id === id_объекта_который_надо_удалить);
    Ответ написан
    1 комментарий
  • Как повесить обработчик события нажатия с передачей информации в блок?

    0xD34F
    @0xD34F Куратор тега JavaScript
    Метод generateCalendar - убрать объявление локальной переменной currDate.

    document.querySelector('.calendar-days').addEventListener('click', e => {
      if (e.target.classList.contains('calendar-day-hover')) {
        currDate = new Date(curr_year.value, curr_month.value, e.target.innerText);
        document.querySelector('.heading').innerText = currDate.toLocaleDateString('ru-RU');
        e.currentTarget.querySelector('.curr-date')?.classList.remove('curr-date');
        e.target.classList.add('curr-date');
      }
    });
    Ответ написан
    7 комментариев
  • Как найти самую ближайшую точку из массива?

    0xD34F
    @0xD34F Куратор тега JavaScript
    const point = { latitude: lat, longtitude: lon };
    const closest = arr.reduce((closest, n) => {
      const d = sphericalDistance(point, n);
      return d < closest[1] ? [ n, d ] : closest;
    }, [ null, Infinity ])[0];
    
    
    function sphericalDistance(p1, p2) {
      // https://en.wikipedia.org/wiki/Great-circle_distance
    }
    Ответ написан
    Комментировать
  • Как реализовать кнопку "показать еще" с раскрытием следующих блоков на jquery?

    0xD34F
    @0xD34F Куратор тега JavaScript
    const SHOW_INITIAL = 2;
    const SHOW_MORE = 3;
    const $button = $('.btn-search-more');
    const $items = $('.catalog-list__items-top').hide();
    
    showItems(SHOW_INITIAL);
    $button.click(() => showItems(SHOW_MORE));
    
    function showItems(count) {
      const $hidden = $items.filter(':hidden');
      $hidden.slice(0, count).show();
      $button.toggle($hidden.length > count);
    }
    Ответ написан
    Комментировать
  • Как получить названия всех форм на странице в виде массива?

    0xD34F
    @0xD34F Куратор тега JavaScript
    const result = Array.prototype.flatMap.call(
      document.forms,
      function(form) {
        return form.className.match(this) ?? [];
      },
      /(?<=(^| )js-)[a-z-]*(?=-form( |$))/g
    );

    или

    const result = [];
    const reg = /^js-([a-z-]*)-form$/;
    
    for (const form of document.querySelectorAll('form')) {
      for (const cls of form.classList) {
        const name = reg.exec(cls)?.[1];
        if (name != null) {
          result.push(name);
        }
      }
    }
    Ответ написан
    Комментировать
  • Как спрятать пустую категорию списка при фильтрации?

    0xD34F
    @0xD34F Куратор тега JavaScript
    $('#cityFilter').on('input', function() {
      const value = this.value.toLowerCase();
    
      $('#city-list .city')
        .hide()
        .filter((i, n) => $(n).text().toLowerCase().includes(value))
        .show();
    
      $('#city-list .state')
        .hide()
        .filter((i, n) => $(n).nextUntil('.state').is(':visible'))
        .show();
    });

    или

    document.querySelector('#cityFilter').addEventListener('input', e => {
      const value = e.target.value.toLowerCase();
    
      document.querySelectorAll('#city-list li').forEach(function(n) {
        if (n.matches('.state')) {
          this.splice(0, 2, n, true);
        } else {
          const hide = n.textContent.toLowerCase().indexOf(value) === -1;
          this[1] &&= hide;
          n.classList.toggle('hidden', hide);
          if (!n.nextElementSibling?.matches('.city')) {
            this[0].classList.toggle('hidden', this[1]);
          }
        }
      }, [ null, true ]);
    });
    Ответ написан
    1 комментарий
  • Как назначить класс всем ссылкам в блоке, кроме первой?

    0xD34F
    @0xD34F Куратор тега JavaScript
    document.querySelectorAll('.col').forEach(n => {
      n.querySelectorAll('a').forEach((m, i) => m.classList.toggle('d-none', !!i));
    });

    или (если перед первыми ссылками других элементов нет, и сами ссылки ни во что дополнительно не обёрнуты)

    document.querySelectorAll('.col a').forEach(n => {
      n.classList.toggle('d-none', !!n.previousElementSibling);
    });
    Ответ написан
  • Как получить результаты нескольких первых выполненных Promise?

    0xD34F
    @0xD34F Куратор тега JavaScript
    const getFisrtPromises = (promises, count) =>
      new Promise(resolve => {
        const results = [];
    
        const onResolve = result =>
          results.length < count &&
          [ count, promises.length ].includes(results.push(result)) &&
          resolve(results);
    
        promises.forEach(n => n.then(onResolve));
      });
    Ответ написан
    Комментировать
  • Как на Jquery добавлять и удалять идентичные значения (текст) в блок?

    0xD34F
    @0xD34F Куратор тега JavaScript
    const filterSelector = '.js-filter';
    const itemSelector = '.dfth__item';
    const checkboxSelector = '.dfth__check:checked';
    const labelSelector = '.dfth__label';
    const resetSelector = '.dfth__reset';
    const selectedSelector = '.js-type-result';
    const defaultValue = 'ничего не выбрано';

    Вот jquery:

    const $filter = $(filterSelector).change(function() {
      const selected = $(checkboxSelector, this)
        .closest(itemSelector)
        .find(labelSelector)
        .get()
        .map(n => $(n).text())
        .join(', ');
    
      $(selectedSelector, this).text(selected || defaultValue);
    });
    
    $filter.find(resetSelector).click(() => {
      $filter.find(checkboxSelector).prop('checked', false);
      $filter.trigger('change');
    });

    А вот jquery нет:

    const filter = document.querySelector(filterSelector);
    
    filter.addEventListener('change', ({ currentTarget: ct }) => {
      const selected = Array
        .from(
          ct.querySelectorAll(checkboxSelector),
          n => n.closest(itemSelector).querySelector(labelSelector).textContent)
        .join(', ');
    
      ct.querySelector(selectedSelector).textContent = selected || defaultValue;
    });
    
    filter.querySelector(resetSelector).addEventListener('click', () => {
      filter.querySelectorAll(checkboxSelector).forEach(n => n.checked = false);
      filter.dispatchEvent(new Event('change'));
    });
    Ответ написан
    1 комментарий
  • Как проверить что в коллекции map содержатся два одинаковых элемента?

    0xD34F
    @0xD34F Куратор тега JavaScript
    Проверяем факт наличия дубликатов:

    const hasDuplicates = map.size > new Set(Array.from(map, n => n[1].id)).size;

    Смотрим, кто конкретно повторяется:

    const duplicates = Array
      .from([...map].reduce((acc, [ , { id } ]) => acc.set(id, acc.has(id)), new Map))
      .reduce((acc, n) => (n[1] && acc.push(n[0]), acc), []);

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

    const count = Array
      .from(map.values())
      .reduce((acc, { id }) => acc.set(id, -~acc.get(id)), new Map);
    Ответ написан
    2 комментария
  • Как назначать класс активному элементу внутри блока независимо от других блоков?

    0xD34F
    @0xD34F Куратор тега JavaScript
    Где элементы находятся, что за элементы, какой класс у них надо переключать:

    const containerSelector = '.block-btn';
    const itemSelector = `${containerSelector} [data-cost]`;
    const activeClass = 'active';

    Какие тут возможны варианты:

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

    const onClick = ({ currentTarget: t }) => t
      .closest(containerSelector)
      .querySelectorAll(itemSelector)
      .forEach(n => n.classList.toggle(activeClass, n === t));
    
    document.querySelectorAll(itemSelector).forEach(n => {
      n.addEventListener('click', onClick);
    });

    Делегированный обработчик клика назначается контейнерам. Проверяем, что кликнут был интересующий нас элемент (пробуем до него подняться от элемента, на котором случилось событие). Если да, ищем внутри контейнера активный элемент, снимаем класс. Кликнутому элементу класс добавляем:

    for (const n of document.querySelectorAll(containerSelector)) {
      n.addEventListener('click', onClick);
    }
    
    function onClick(e) {
      const item = e.target.closest(itemSelector);
      if (item) {
        this.querySelector(`.${activeClass}`)?.classList.remove(activeClass);
        item.classList.add(activeClass);
      }
    }

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

    document.addEventListener('click', ({ target: t }) => t
      .closest(itemSelector)
      ?.closest(containerSelector)
      .querySelectorAll(itemSelector)
      .forEach(n => n.classList.toggle(activeClass, n.contains(t)))
    );
    Ответ написан
    2 комментария
  • Как получить разделитель даты?

    0xD34F
    @0xD34F Куратор тега JavaScript
    Разделитель - первый символ, не являющийся цифрой. Так что

    const delimiter = str.match(/\D/)[0];
    
    // или
    
    const delimiter = str[str.search(/\D/)];
    
    // или
    
    const [ delimiter ] = str.replace(/^\d+/, '');
    
    // или
    
    const delimiter = Array.prototype.find.call(str, n => Number.isNaN(+n));
    
    // или
    
    const delimiter = [...str].filter(n => !'0123456789'.includes(n)).shift();
    Ответ написан
    Комментировать
  • Как добавить класс всем блокам, которые выводятся после 3го блока?

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

    const parent = document.querySelector('.container');
    const className = 'green';
    const startFrom = 4;

    Добавляем:

    parent
      .querySelectorAll(`:scope > :nth-child(n + ${startFrom + 1})`)
      .forEach(n => n.classList.add(className));
    
    // или
    
    for (const n of Array.prototype.slice.call(parent.children, startFrom)) {
      n.classList.add(className);
    }
    
    // или
    
    for (let el = parent.children[startFrom]; el; el = el.nextElementSibling) {
      el.classList.add(className);
    }
    
    // или, также удаляем класс (если вдруг есть) у тех, кому он не должен быть добавлен
    
    for (let i = 0; i < parent.children.length; i++) {
      parent.children[i].classList.toggle(className, i >= startFrom);
    }
    Ответ написан
    1 комментарий
  • Как сгенерировать массив только из уникальных чисел определенной длины?

    0xD34F
    @0xD34F Куратор тега JavaScript
    Какие есть способы создания массива:

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

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

    • Кидать ошибку.
    • Создавать массив с длиной меньше указанной.

    Вот как-то так:

    function createRandomArr(length, min, max) {
      if (length > max - min + 1) {
        throw 'такого массива быть не может';
      }
    
      const values = new Set;
      for (; values.size < length; values.add(min + Math.random() * (max - min + 1) | 0)) ;
      return [...values];
    }

    const createRandomArr = (length, min, max) => Array
      .from({ length }, function() {
        return this.splice(Math.random() * this.length | 0, 1);
      }, Array.from({ length: max - min + 1 }, (n, i) => i + min))
      .flat();

    function createRandomArr(length, min, max) {
      const arr = Array.from({ length: max - min + 1 }, (n, i) => min + i);
    
      for (let i = arr.length; --i > 0;) {
        const j = Math.random() * (i + 1) | 0;
        [ arr[i], arr[j] ] = [ arr[j], arr[i] ];
      }
    
      return arr.slice(0, length);
    }
    Ответ написан
    Комментировать
  • Как можно сократить запись создания одинаковых элементов?

    0xD34F
    @0xD34F Куратор тега JavaScript
    Значения собрать в массив: const values = [ id, sum, system, date ];.

    Дальше есть варианты:

    payment.innerHTML = values.map(n => `<td>${n}</td>`).join('');
    
    // или
    
    values.forEach(n => payment.insertCell().textContent = n);
    
    // или
    
    payment.append(...values.map(n => {
      const td = document.createElement('td');
      td.innerText = n;
      return td;
    }));

    Или, то же самое, но с массивом ключей вместо значений:

    - function createPayment({id, sum, system, date}) {
    + function createPayment(data) {

    - const values = [ id, sum, system, date ];
    + const keys = [ 'id', 'sum', 'system', 'date' ];

    - values.forEach(n => payment.insertCell().textContent = n);
    + keys.forEach(n => payment.insertCell().textContent = data[n]);
    Ответ написан
    Комментировать
  • Как рекурсивно обойти древовидный объект?

    0xD34F
    @0xD34F Куратор тега JavaScript
    const getKeys = obj =>
      obj instanceof Object
        ? Object.entries(obj).flatMap(n => [ n[0], ...getKeys(n[1]) ])
        : [];
    Ответ написан
    Комментировать