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

    0xD34F
    @0xD34F Куратор тега JavaScript
    Пытаюсь так:

    let currentIndex = $(".js-practice_button.current").index();

    Но значение всегда 0, у какой бы кнопки класс current не присутствовал.

    Потому что метод index по умолчанию определяет индекс элемента среди соседей, а так как у каждой кнопки есть отдельный родитель... Ну да, получаете то, что получаете.

    Можно вместо индекса кнопки определять индекс родителя:

    const index = $('.js-practice_button.current').closest('li').index();

    Или, если указать методу index в качестве параметра селектор, то индекс будет определятся не среди соседей, а среди элементов, соответствующих селектору:

    const index = $('.js-practice_button.current').index('.js-practice_button');

    А вообще, к чёрту jquery. Есть варианты и на чистом js:

    const index = Array.prototype.findIndex.call(
      document.querySelectorAll('.js-practice_button'),
      n => n.classList.contains('current')
    );
    
    // или
    
    const el = document.querySelector('.js-practice_button.current')?.parentNode;
    const index = el ? [...el.parentNode.children].indexOf(el) : -1;
    
    // или
    
    let index = -1;
    for (
      let el = document.querySelector('.js-practice_button.current')?.closest('li');
      el;
      el = el.previousElementSibling, index++
    ) ;
    Ответ написан
    Комментировать
  • Как удалить из массива числа с повторяющимися цифрами?

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

    const noRepeatingDigits = num => !/(\d).*\1/.test(num);
    
    // или
    
    const noRepeatingDigits = num => -~Math.log10(num) === new Set(`${num}`).size;
    
    // или
    
    const noRepeatingDigits = num => [...'' + num].every((n, i, a) => i === a.indexOf(n));
    
    // или
    
    const noRepeatingDigits = num => String(num)
      .split('')
      .reduce((acc, n) => (acc[n]++, acc), Array(10).fill(0))
      .every(n => n < 2);
    
    // или
    
    const noRepeatingDigits = num =>
      !''.match.call(num, /./g).some(function(n) {
        return this[n] = Object.hasOwn(this, n);
      }, {});
    
    // или
    
    const noRepeatingDigits = num =>
      !Array.from(num.toString()).sort().find((n, i, a) => n === a[i + 1]);

    Теперь массив. Можно удалить ненужное из существующего:

    arr.splice(0, arr.length, ...arr.filter(noRepeatingDigits));
    
    // или
    
    let numDeleted = 0;
    
    for (let i = 0; i < arr.length; i++) {
      const n = arr[i];
      arr[i - numDeleted] = n;
      numDeleted += !noRepeatingDigits(n);
    }
    
    arr.length -= numDeleted;
    
    // или
    
    for (let i = arr.length; i--; ) {
      if (!noRepeatingDigits(arr[i])) {
        arr.splice(i, 1);
      }
    }

    Или собрать новый:

    const newArr = arr.filter(noRepeatingDigits);
    
    // или
    
    const newArr = [];
    
    for (const n of arr) {
      if (noRepeatingDigits(n)) {
        newArr.push(n);
      }
    }
    
    // или
    
    const newArr = [];
    
    for (let i = 0; i < arr.length; i++) {
      if (noRepeatingDigits(arr[i])) {
        newArr[newArr.length] = arr[i];
      }
    }
    
    // или
    
    const newArr = (function xxx(arr, i = 0) {
      return i < arr.length
        ? (noRepeatingDigits(arr[i]) ? [ arr[i] ] : []).concat(xxx(arr, i + 1))
        : [];
    })(arr);
    Ответ написан
    1 комментарий
  • Как сделать равномерное слияние многомерного массива?

    0xD34F
    @0xD34F Куратор тега JavaScript
    const flatZip = arrs => arrs
      .reduce((acc, arr) => (
        arr.forEach((n, i) => (acc[i] ??= []).push(n)),
        acc
      ), [])
      .flat();
    
    
    const result = flatZip([
      [ 1, 2, 3, 4 ],
      [ 5, 6, 7, 8 ],
      [ 9, 10, 11 ],
    ]);
    Ответ написан
    Комментировать
  • Как отфильтровать элементы li по объекту?

    0xD34F
    @0xD34F Куратор тега JavaScript
    const li = Array.prototype.filter.call(
      document.querySelector('ul').children,
      function(n) {
        return this.every(([ k, v ]) => v === n.querySelector(`.${k}`).innerText);
      },
      Object.entries(items)
    );

    или

    const li = [];
    
    COLLECT_LI:
    for (const n of document.querySelectorAll('li')) {
      for (const k in items) {
        if (Object.hasOwn(items, k) && items[k] !== n.querySelector('.' + k).textContent) {
          continue COLLECT_LI;
        }
      }
    
      li.push(n);
    }
    Ответ написан
    4 комментария
  • Как удалять теги из массива, которые уже не используются?

    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 url of urls) {
        try {
          return await loadImage(url);
        } catch (e) {}
      }
    
      throw 'ничего загрузить не удалось';
    }
    
    
    getFirstLoadedImage(resolutions.map(n => `https://i.ytimg.com/vi/${videoId}/${n}.jpg`))
      .then(img => document.body.append(img))
      .catch(console.error);

    или, без async/await:

    const getFirstLoadedImage = urls =>
      new Promise((resolve, reject) => {
        (function next(i) {
          if (i === urls.length) {
            reject('ничего загрузить не удалось');
          } else {
            loadImage(urls[i]).then(resolve, () => next(i + 1));
          }
        })(0);
      });
    Ответ написан
    Комментировать
  • Как связать свою кнопку с соответствующим маркером 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 classContainer = 'selectboxss';
    const classOption = 'selectoption';
    const getClasses = el => [...el.classList].filter(n => n !== classOption);
    //const getClass = el => el.className.match(RegExp(`${classOption}-\\d+`))[0];
    
    document.addEventListener('click', ({ target: t }) => {
      if (t.classList.contains(classOption)) {
        const container = t.closest(`.${classContainer}`);
        const { classList } = container;
        classList.remove(...[...container.querySelectorAll(`.${classOption}`)].flatMap(getClasses));
        classList.add(...getClasses(t));
        //classList.remove(...Array.from(container.querySelectorAll(`.${classOption}`), getClass));
        //classList.add(getClass(t));
      }
    });
    Ответ написан
    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;
    
    document.addEventListener('click', e => {
      const button = e.target.closest(buttonSelector);
      if (button) {
        const items = [...button.parentNode.querySelectorAll(notActiveSelector)];
        items.slice(0, newActiveCount).forEach(n => n.classList.add(activeClass));
        button.disabled = items.length <= newActiveCount;
      }
    });
    Ответ написан
    Комментировать
  • Как написать регулярное выражение для получения даты через точку?

    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 groupedArr = arr.reduce((acc, n) => {
      const keys = n.slice(0, -1);
      let group = acc.find(m => m.length === n.length && keys.every((k, i) => k === m[i]));
      if (!group) {
        acc.push(group = [ ...keys, 0 ]);
      }
      group[keys.length] += n[keys.length];
      return acc;
    }, []);

    или

    const groupedArr = arr.reduce((acc, n) => {
      const keys = n.slice(0, -1);
      const groups = keys.reduce((groups, k) => groups.set(k, groups.get(k) ?? new Map).get(k), acc[1]);
      let group = groups.get(acc[2]);
      if (!group) {
        acc[0].push(group = [ ...keys, 0 ]);
        groups.set(acc[2], group);
      }
      group[keys.length] += n[keys.length];
      return acc;
    }, [ [], new Map, Symbol() ])[0];
    Ответ написан
    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 result = [];
      const stack = [];
    
      for (
        let i = 0, source = [ [ 0, value ] ], target = result;
        i < source.length || stack.length;
        i++
      ) {
        if (i === source.length) {
          [ i, source, target ] = stack.pop();
        } else {
          const [ k, v ] = source[i];
          const isObject = v instanceof Object;
          const newK = target instanceof Array ? k : replacer(k);
    
          target[newK] = isObject ? v.constructor() : v;
    
          if (isObject) {
            stack.push([ i, source, target ]);
            [ i, source, target ] = [ -1, Object.entries(v), target[newK] ];
          }
        }
      }
    
      return result[0];
    }
    Ответ написан
    Комментировать