Ответы пользователя по тегу JavaScript
  • Как удалять теги из массива, которые уже не используются?

    0xD34F
    @0xD34F Куратор тега JavaScript
    Зайдите с другой стороны - вместо удаления того, чего больше нет, получите то, что есть, и замените массив:

    tags = Array.from(new Set(data.flatMap(n => n.tags)));

    Если массив по какой-то причине заменять нельзя, тогда удалите все элементы из существующего и запишите новые:

    tags.splice(0, tags.length, ...new Set(data.flatMap(n => n.tags)));
    Ответ написан
    Комментировать
  • Как пытаться поочерёдно загружать изображения, до первого успешно загруженного?

    0xD34F
    @0xD34F Куратор тега JavaScript
    Асинхронная функция, внутри цикл:

    async function getFirstLoadedImage(urls) {
      for (const n of urls) {
        try {
          return await loadImage(n);
        } catch (e) {}
      }
    
      throw 'ничего загрузить не удалось';
    }

    Или, рекурсия:

    const getFirstLoadedImage = (urls, i = 0) => i < urls.length
      ? loadImage(urls[i]).catch(() => getFirstLoadedImage(urls, -~i))
      : Promise.reject('ничего загрузить не удалось');

    Пользоваться этим, понятное дело, так:

    getFirstLoadedImage(resolutions.map(n => `https://i.ytimg.com/vi/${videoId}/${n}.jpg`))
      .then(img => document.body.append(img))
      .catch(console.error);
    Ответ написан
    Комментировать
  • Как связать свою кнопку с соответствующим маркером google maps?

    0xD34F
    @0xD34F Куратор тега JavaScript
    Маркеры сложить в массив, по клику на кнопки доставать из массива маркер с тем же индексом, что и у кнопки, триггерить на нём событие клика. Как-то так.
    Ответ написан
    Комментировать
  • Как распределить элементы (объекты) массива в ОБЪЕКТ с объектами (не сортированными) по убывающей сумме данных из объектов ОБЪЕКТА?

    0xD34F
    @0xD34F Куратор тега JavaScript
    const result = DATASETS
      .map(n => [ n, n.data.reduce((acc, m) => acc + m, 0) ])
      .sort((a, b) => a[1] - b[1])
      .slice(-colors.length)
      .map((n, i, a) => ({ ...n[0], ...colors[a.length - i - 1] }));
    Ответ написан
    Комментировать
  • Как массив объектов собрать в один объект, объединив одноимённые свойства в массивы?

    0xD34F
    @0xD34F Куратор тега JavaScript
    Получаем все возможные ключи; под каждый создаём пустой массив; при обработке объекта из исходного массива бежим по массиву ключей, добавляем в соответствующий массив значение из объекта или дефолтное - в зависимости от наличия ключа в объекте:

    function groupValues(arr, defaultValue = null) {
      const keys = [...new Set(arr.flatMap(Object.keys))];
      return arr.reduce((acc, n) => {
        keys.forEach(k => acc[k].push(Object.hasOwn(n, k) ? n[k] : defaultValue));
        return acc;
      }, Object.fromEntries(keys.map(k => [ k, [] ])));
    }
    
    
    const result = groupValues(arr);

    Или, результирующий объект изначально пуст; при обработке объекта из исходного массива перебираем его ключи; если ключ отсутствует в результирующем объекте, создаём массив с длиной как у исходного, заполняем его дефолтным значением; записываем в массив в результирующем объекте значение под тем же индексом, который имеет обрабатываемый объект в исходном массиве:

    const groupValues = (arr, defaultValue = null) =>
      arr.reduce((acc, n, i, a) => (
        Object
          .keys(n)
          .forEach(k => (acc[k] ??= Array(a.length).fill(defaultValue))[i] = n[k]),
        acc
      ), {});
    Ответ написан
    Комментировать
  • Как при клике на li передать его добавочный класс родительскому диву?

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

    const containerSelector = '.selectboxss';
    const itemClass = 'selectoption';
    const itemSelector = `${containerSelector} .${itemClass}`;
    
    const getClasses = el => Array.prototype.filter.call(el.classList, n => n !== itemClass);
    //const getClass = el => el.className.match(RegExp(`${itemClass}-\\d+`))[0];
    
    function updateClasses(item) {
      const container = item.closest(containerSelector);
      const { classList: cl } = container;
      cl.remove(...Array.prototype.flatMap.call(container.querySelectorAll(itemSelector), getClasses));
      cl.add(...getClasses(item));
      //cl.remove(...Array.from(container.querySelectorAll(itemSelector), getClass));
      //cl.add(getClass(li));
    }

    Назначаем обработчик клика каждому элементу индивидуально:

    document.querySelectorAll(itemSelector).forEach(function(n) {
      n.addEventListener('click', this);
    }, e => updateClasses(e.currentTarget));

    Или, можем применить делегирование:

    document.addEventListener('click', e => {
      const item = e.target.closest(itemSelector);
      if (item) {
        updateClasses(item);
      }
    });
    Ответ написан
    5 комментариев
  • Как применить рекурсию для получения данных из DOM?

    0xD34F
    @0xD34F Куратор тега JavaScript
    Главный косяк:

    tableValues(element);

    Как вы собираетесь перебирать элемент? Это абсурд. Должно быть element.children/element.childNodes.

    Косяки помельче:

    document.querySelector('form').childNodes

    Поскольку вам нужны только input'ы и select'ы, то не надо перебирать заведомо лишнее - используйте children вместо childNodes. Ну и погуглите, в чём между ними разница.

    if (element.nodeType == 1 && element.nodeName == 'INPUT' || element.nodeName == 'SELECT') {

    Проверка значения nodeType лишняя. Кроме того, вам не помешает разобраться с приоритетом выполнения операторов - nodeType вы тут проверяете только для input'а.

    let result = [];

    Почему эта штука объявляется вне функции, а наполняется внутри? Что, перед каждым вызовом функции будете вручную обнулять результат? Надо создавать массив со значениями внутри функции, и возвращать его как результат её выполнения.

    Ну и конечно всё это делается несколько короче:

    const getValues = el =>
      el instanceof Element
        ? [ 'INPUT', 'SELECT' ].includes(el.tagName)
          ? [ el.value ]
          : [...el.children].flatMap(getValues)
        : [];
    
    // или
    
    const getValues = el =>
      [ 'INPUT', 'SELECT' ].includes(el?.tagName)
        ? [ el.value ]
        : [].flatMap.call(el.children ?? [], getValues);
    
    
    
    const values = getValues(document.querySelector('form'));

    Или ещё короче - если без рекурсии:

    const values = Array.from(document.querySelector('form').elements, n => n.value);

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

    const getFromDOMNodes = (node, test, getVal) =>
      node instanceof Node
        ? Array.prototype.reduce.call(
            node.childNodes,
            (acc, n) => (acc.push(...getFromDOMNodes(n, test, getVal)), acc),
            test(node) ? [ getVal(node) ] : []
          )
        : [];
    
    
    const values = getFromDOMNodes(
      document.querySelector('form'),
      node => [ 'INPUT', 'SELECT' ].includes(node.nodeName),
      node => node.value
    );
    Ответ написан
    1 комментарий
  • Как заменить значения через js в по нажатию кнопки?

    0xD34F
    @0xD34F Куратор тега JavaScript
    const values = {
      RUS: [ 'раз значение', 'два значение', 'три' ],
      US: [ '...', '...', '...' ],
      // ...
    };
    
    const items = document.querySelectorAll('li');
    const buttons = document.querySelectorAll('button');
    
    buttons.forEach(n => n.addEventListener('click', onClick));
    
    function onClick({ target: { innerText: key } }) {
      items.forEach(function(n, i) {
        n.innerText = this[i] ?? `ЗНАЧЕНИЕ #${i} ДЛЯ ${key} НЕ ЗАДАНО`;
      }, values[key] ?? []);
    }
    Ответ написан
    2 комментария
  • Как получить из строки все ссылки?

    0xD34F
    @0xD34F Куратор тега JavaScript
    str.replace(/\[(.+?)\]\((.+?)\)/g, '<a href="$2">$1</a>')
    Ответ написан
    Комментировать
  • Как сделать что-бы при клике на кнопку блоки добавлялись по очереди?

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

    const buttonSelector = '.added';
    const itemSelector = '.added-block';
    const activeClass = '_active';
    const notActiveSelector = `${itemSelector}:not(.${activeClass})`;
    const newActiveCount = 2;
    
    function onButtonClick(button) {
      const items = [...button.parentNode.querySelectorAll(notActiveSelector)];
      items.slice(0, newActiveCount).forEach(n => n.classList.add(activeClass));
      button.disabled = items.length <= newActiveCount;
    }

    Назначаем обработчик клика каждой кнопке индивидуально:

    document.querySelectorAll(buttonSelector).forEach(function(n) {
      n.addEventListener('click', this);
    }, e => onButtonClick(e.currentTarget));

    Или, применяем делегирование:

    document.addEventListener('click', e => {
      const button = e.target.closest(buttonSelector);
      if (button) {
        onButtonClick(button);
      }
    });
    Ответ написан
    Комментировать
  • Как написать регулярное выражение для получения даты через точку?

    0xD34F
    @0xD34F Куратор тега JavaScript
    Выдернуть подстроки, состоящие из цифр, склеить через точку:

    str.match(/\d+/g).join('.')
    Ответ написан
    3 комментария
  • Как получить подстроку между последними "/"?

    0xD34F
    @0xD34F Куратор тега JavaScript
    /[^\/]+(?=\/$)/.exec(str)[0]

    или

    str.match(/[^\/]+/g).pop()

    или

    str.split('/').at(-2)
    Ответ написан
    1 комментарий
  • Как в javascript перехватывать вызов методов объекта?

    0xD34F
    @0xD34F Куратор тега JavaScript
    Хук get не позволяет получить параметры вызова метода

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

    function sequence(functions) {
      return new Proxy(functions, {
        get(target, key) {
          const val = target[key];
          return val instanceof Function
            ? (...args) => {
                console.log(`${key} called with arguments: `, args);
                return val.apply(target, args);
              }
            : val;
        },
      });
    }
    Ответ написан
    Комментировать
  • Как просуммировать вложенные массивы?

    0xD34F
    @0xD34F Куратор тега JavaScript
    const uniqueWithSum = arr =>
      arr.reduce((acc, n) => {
        const keys = n.slice(0, -1);
        const item = acc.find(m => m.length === n.length && keys.every((k, i) => k === m[i]));
        (item ?? (acc[acc.length] = [ ...keys, 0 ]))[keys.length] += n[keys.length];
        return acc;
      }, []);

    или

    const uniqueWithSum = (function(arr) {
      const indexTree = new Map;
      return arr.reduce((acc, [...keys]) => {
        const val = keys.pop();
        const indexes = keys.reduce((p, c) => p.set(c, p.get(c) ?? new Map).get(c), indexTree);
        const index = indexes.set(this, indexes.get(this) ?? ~-acc.push([ ...keys, 0 ])).get(this);
        acc[index][keys.length] += val;
        return acc;
      }, []);
    }).bind(Symbol());

    или

    const uniqueWithSum = arr =>
      [...arr.reduce((acc, n) => {
        const end = n.length - 1;
        const key = n.reduce((p, c, i) => i === end ? p : p.set(c, p.get(c) ?? new Map).get(c), acc[0]);
        acc[1].set(key, acc[1].get(key) ?? n.map((m, i) => i !== end && m)).get(key)[end] += n[end];
        return acc;
      }, [ new Map, new Map ])[1].values()];
    Ответ написан
    1 комментарий
  • Как заменить ключи во вложенных объектах?

    0xD34F
    @0xD34F Куратор тега JavaScript
    Рекурсия есть:

    const replaceKeys = (value, replacer) =>
      value instanceof Object
        ? value instanceof Array
          ? value.map(n => replaceKeys(n, replacer))
          : Object.fromEntries(Object
              .entries(value)
              .map(n => [ replacer(n[0]), replaceKeys(n[1], replacer) ])
            )
        : value;
    
    
    const newObj = replaceKeys(obj, k => `${k}_upd`);

    Рекурсии нет:

    function replaceKeys(value, replacer) {
      const stack = [];
      const clones = new Map;
      const getClone = val => val instanceof Object
        ? (clones.has(val) || stack.push([ val, clones.set(val, val.constructor()).get(val) ]),
           clones.get(val))
        : val;
    
      for (getClone(value); stack.length;) {
        const [ source, target ] = stack.pop();
        const isArray = Array.isArray(source);
        for (const k in source) if (Object.hasOwn(source, k)) {
          target[isArray ? k : replacer(k)] = getClone(source[k]);
        }
      }
    
      return getClone(value);
    }
    Ответ написан
    Комментировать
  • Как можно заменить цвет #a8a3a0 на пример на квадратик белого цвета?

    0xD34F
    @0xD34F Куратор тега JavaScript
    const regex = /#[a-f\d]+;$/i;
    const replacement = '<span class="color" style="background: $&"></span>';
    
    document.querySelectorAll('селектор сами сообразите').forEach(n => {
      n.innerHTML = n.innerText.replace(regex, replacement);
    });
    Ответ написан
    2 комментария
  • Как разбить массив на подмассивы, отсортировать по рядам упорядоченных чисел?

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

    const result = arr.reduce((acc, n, i, a) => (
      n === a[i - 1] + 1 || acc.push([]),
      acc.at(-1).push(n),
      acc
    ), []);

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

    function groupAdjacent(
      data,
      {
        key = n => n,
        newGroup = (c, p) => c !== p,
      } = {}
    ) {
      const result = [];
      const getVal = key instanceof Function ? key : n => n[key];
      let prev = null;
      let i = -1;
    
      for (const n of data) {
        const curr = getVal(n, ++i);
    
        if (!result.length || newGroup(curr, prev)) {
          result.push([]);
        }
    
        result.at(-1).push(n);
        prev = curr;
      }
    
      return result;
    }

    Как применять в вашем случае:

    const result = groupAdjacent(arr, { newGroup: (c, p) => c !== -~p });
    // или
    const result = groupAdjacent(arr, { key: (n, i) => n - i });

    Какие ещё возможны способы применения:

    groupAdjacent(arr, { key: 'name' })
    groupAdjacent(arr, { newGroup: n => n === 1 })
    groupAdjacent(arr).map(n => n.length > 1 ? n : n[0])
    groupAdjacent(arr, { newGroup: n => /^\d+\./.test(n) })
    Ответ написан
    5 комментариев
  • Почему модальное окно всплывает только при нажатие на первую кнопку?

    0xD34F
    @0xD34F Куратор тега JavaScript
    Всплывает при нажатии на все кнопки. Но не то, которое надо, а первое. Которое видно только когда отображается первый же .product. Вместо первого .product нужен тот, внутри которого находится нажатая кнопка:

    document.querySelector('.wrapper_product') ---> event.target
    Ответ написан
    4 комментария
  • Как скрывать текущий item(li) при открытие другого item(li)?

    0xD34F
    @0xD34F Куратор тега JavaScript
    Если прямо отвечать на спрошенное, то...
    menu.addEventListener('click', ({ target: t }) => {
      if (t.tagName === 'SPAN') {
        const parent = t.parentNode;
        parent.classList.toggle('active');
        for (const n of menu.querySelectorAll('.active')) {
          if (n !== parent) {
            n.classList.toggle('active', n.contains(parent));
          }
        }
      }
    });

    Но вообще, давайте-ка напишем чуть более универсальное решение.

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

    - .menu_list_item.active .submenu {
    + .active > .submenu {

    - .submenu_list_item.active .product {
    + .active > .product {

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

    const container = document.querySelector('#menu');
    const itemSelector = 'li';
    const buttonSelector = `${itemSelector} span`;
    const activeClass = 'active';
    
    container.addEventListener('click', function(e) {
      const item = e.target.closest(buttonSelector)?.closest(itemSelector);
      if (item) {
        item.classList.toggle(activeClass);
        this.querySelectorAll(`.${activeClass}`).forEach(n => {
          if (n !== item) {
            n.classList.toggle(activeClass, n.contains(item));
          }
        });
      }
    });
    Ответ написан
    3 комментария