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

    0xD34F
    @0xD34F Куратор тега JavaScript
    const parseValue = str => {
      const values = str.match(/[^\s,]+/g) || [ null ];
      return values.length > 1 ? values : values[0];
    };
    
    const parseStyleStr = str =>
      str.split(';').reduce((acc, n) => {
        const [ k, v ] = n.split(':').map(n => n.trim());
        if (k && v) {
          const f = [...v.matchAll(/([\w]+?)\((.+?)\)/g)];
          acc[k] = f.length
            ? Object.fromEntries(f.map(n => [ n[1], parseValue(n[2]) ]))
            : parseValue(v);
        }
    
        return acc;
      }, {});
    Ответ написан
    Комментировать
  • Как по нажатию на одну вкладку, скрыть другие вкладки?

    0xD34F
    @0xD34F Куратор тега CSS
    Хорошо бы если на чистом css

    Придётся сильно поменять разметку. Управлять видимостью вкладок можно с помощью скрытых радиокнопок.

    но и js тоже можно

    document.addEventListener('click', ({ target: { tagName, parentNode: p } }) => {
      if (tagName === 'SUMMARY') {
        document.querySelectorAll('details').forEach(n => n.open = n.open && n === p);
      }
    });
    
    // или
    
    const details = document.querySelectorAll('details');
    const onClick = e => details.forEach(n => n.open = n.open && n === e.target.parentNode);
    details.forEach(n => n.children[0].addEventListener('click', onClick));
    Ответ написан
    1 комментарий
  • Как проверить наличие объекта в массиве?

    0xD34F
    @0xD34F Куратор тега JavaScript
    const obj = arr.find(n => n.id === newObj.id);
    if (obj) {
      obj.counter++;
    } else {
      arr.push({ ...newObj, counter: 1 });
    }
    Ответ написан
    1 комментарий
  • Как выполнить запрос только один раз?

    0xD34F
    @0xD34F Куратор тега JavaScript
    Если отвечать ровно на то, что было спрошено - замените on на one. Но что если запрос окажется неудачным? Наверное, надо оставить пользователю возможность повторить действие. Вон, у вас там класс добавляется - так проверяйте его наличие, если уже есть, ничего делать не надо; также добавьте обработку неудавшихся запросов, там надо будет класс снять.
    Ответ написан
    Комментировать
  • Как объединить два массива в один у которых ключи id и user_id с одинаковыми значениями?

    0xD34F
    @0xD34F Куратор тега JavaScript
    Object.values(arr1.reduce((acc, { user_id: id, ...n }) => {
      Object.entries(n).forEach(([ k, v ]) => acc[id][k] = (acc[id][k] || 0) + v);
      return acc;
    }, Object.fromEntries(arr2.map(n => [ n.id, {...n} ]))))

    если надо, чтобы в исходном и результирующем массивах элементы с одинаковыми id располагались на одних и тех же местах, то вместо обычного объекта собираем данные в Map:

    [...arr1.reduce((acc, { user_id: id, ...n }) => {
      const obj = acc.get(id);
      Object.entries(n).forEach(([ k, v ]) => obj[k] = (obj[k] || 0) + v);
      return acc;
    }, new Map(arr2.map(n => [ n.id, {...n} ]))).values()]

    или, вместо Object.values извлекаем данные из объекта через map по исходному массиву:

    arr2.map(function(n) {
      return this[n.id];
    }, arr1.reduce((acc, { user_id: id, ...n }) => (
      Object.keys(n).forEach(k => acc[id][k] = (acc[id][k] || 0) + n[k]),
      acc
    ), Object.fromEntries(arr2.map(n => [ n.id, {...n} ]))))
    Ответ написан
    Комментировать
  • Как сделать рекурсию в таком случае?

    0xD34F
    @0xD34F Куратор тега JavaScript
    Вспомогательная функция, очень нужна, не спрашивайте зачем:

    const getReversedPaths = (arr, path = []) =>
      arr.reduce((acc, { childItems, ...item }) => {
        path.push(item);
    
        if (childItems) {
          acc.push(...getReversedPaths(childItems, path));
        } else {
          acc.push(path.length > 1
            ? path.map(({ packingLevel }, i, a) => ({ ...a[a.length - i - 1], packingLevel }))
            : [ path[0] ]
          );
        }
    
        path.pop();
    
        return acc;
      }, []);

    Туда:

    const reverseThere = arr =>
      getReversedPaths(arr).map(n =>
        n.reduceRight((child, parent) => ({ ...parent, childItems: [ child ] }))
      );

    Обратно:

    const reverseBackAgain = arr =>
      (function createTree(arr) {
        return Object.values(arr.reduce((acc, [ head, ...tail ]) => {
          if (tail.length) {
            (acc[head.name] = acc[head.name] || { ...head, childItems: [] }).childItems.push(tail);
          } else {
            acc[head.name] = head;
          }
    
          return acc;
        }, {})).map(n => (n.childItems && (n.childItems = createTree(n.childItems)), n));
      })(getReversedPaths(arr));
    Ответ написан
    3 комментария
  • Как в массиве объектов оставить только то, что есть в другом массиве?

    0xD34F
    @0xD34F Куратор тега JavaScript
    Создать новый массив:

    const newArr2 = arr2.filter(n => arr1.some(m => m === n.name));
    
    // или
    
    const obj2 = arr2.reduce((acc, n) => (
      (acc[n.name] = acc[n.name] ?? []).push(n),
      acc
    ), {});
    const newArr2 = arr1.flatMap(n => obj2[n] ?? []);
    
    // или
    
    const newArr2 = [];
    for (const n of arr2) {
      for (const m of arr1) {
        if (m === n.name) {
          newArr2.push(n);
          break;
        }
      }
    }

    Удалить элементы существующего:

    arr2.reduceRight((_, n, i, a) => ~arr1.indexOf(n.name) || a.splice(i, 1), null);
    
    // или
    
    arr2.splice(0, arr2.length, ...arr2.filter(function(n) {
      return this.has(n.name);
    }, new Set(arr1)));
    
    // или
    
    arr2.length -= arr2.reduce((acc, n, i, a) => (
      a[i - acc] = n,
      acc + !arr1.includes(n.name)
    ), 0);
    Ответ написан
    Комментировать
  • Как добавить описание select?

    0xD34F
    @0xD34F Куратор тега JavaScript
    Добавьте в начало скрытый option:

    <select>
      <option value="" hidden>здесь ваше описание</option>
      ...

    UPD. А зачем тэг "javascript"? Собираетесь добавлять "описания" динамически? Если так, есть следующие варианты:

    select.innerHTML = '<option value="" hidden>...</option>' + select.innerHTML;

    select.insertAdjacentHTML('afterbegin', '<option value="" hidden>...</option>');

    const option = document.createElement('option');
    option.value = '';
    option.hidden = true;
    option.innerText = '...';
    select.prepend(option);

    const option = new Option('...', '');
    option.style.display = 'none';
    select.insertBefore(option, select.firstElementChild);

    https://jsfiddle.net/bkdcexhw/
    Ответ написан
    Комментировать
  • Как сделать фильтрацию элементов?

    0xD34F
    @0xD34F Куратор тега JavaScript
    <div id="items"></div>

    document.querySelector('.map__filters').addEventListener('change', function() {
      const values = Array.from(
        this.querySelectorAll('.map__checkbox:checked'),
        n => n.value
      );
    
      const filtered = values.length
        ? data.filter(n => values.every(m => n.features.includes(m)))
        : [];
    
      document.querySelector('#items').innerHTML = filtered
        .map(n => `<div>${JSON.stringify(n, null, 2)}</div>`)
        .join('');
    });
    Ответ написан
    Комментировать
  • Как выбрать информацию из двух массивов?

    0xD34F
    @0xD34F Куратор тега JavaScript
    function filter(obj, key, f) {
      const keys = Object.keys(obj);
    
      return (obj[key] || []).reduce((acc, n, i) => (
        f(n) && keys.forEach(k => acc[k].push(obj[k][i])),
        acc
      ), keys.reduce((acc, k) => (acc[k] = [], acc), {}));
    }
    
    const result = filter(obj, 'years', n => 2012 <= n && n <= 2014);

    или

    function filter(obj, f) {
      const keys = Object.keys(obj);
      const length = keys.length && obj[keys[0]].length;
      const result = Object.fromEntries(keys.map(k => [ k, [] ]));
    
      for (let i = 0; i < length; i++) {
        if (f(i, obj)) {
          keys.forEach(k => result[k].push(obj[k][i]));
        }
      }
    
      return result;
    }
    
    const result = filter(obj, (i, { years: { [i]: n } }) => 2012 <= n && n <= 2014);
    Ответ написан
    1 комментарий
  • Как переключать классы у input в зависимости от длины его значения?

    0xD34F
    @0xD34F Куратор тега JavaScript
    Каким input'ам, по какому событию и какие надо переключать классы, какова минимально допустимая длина содержимого:

    const selector = '[type="tel"]';
    const event = 'input';
    const minLength = 10;
    const classNames = [ 'какой-то класс', 'какой-то другой класс' ];

    Переключаем:

    $(selector).on(event, function() {
      const isLengthOK = $(this).val().length >= minLength;
      $(this)
        .toggleClass(classNames[0], !isLengthOK)
        .toggleClass(classNames[1], isLengthOK);
    });

    или

    document.querySelectorAll(selector).forEach(function(n) {
      n.addEventListener(event, this);
    }, ({ target: t }) => {
      const isLengthOK = +(t.value.length >= minLength);
      classNames.forEach((n, i) => t.classList.toggle(n, isLengthOK === i));
    });
    Ответ написан
    Комментировать
  • Как закрыть 2 одинаковых попапа?

    0xD34F
    @0xD34F Куратор тега JavaScript
    - .dropdown__block.open {
    + .dropdown.open .dropdown__block {

    const containerSelector = '.dropdown';
    const buttonSelector = `${containerSelector} .dropdown__toggler`;
    const activeClass = 'open';
    
    
    // jquery, как вы и хотели
    $(document).on('click', ({ target: t }) => {
      const $container = $(t).closest(containerSelector);
      const $button = $(t).closest(buttonSelector);
    
      if ($button.length) {
        $container.toggleClass(activeClass);
      }
    
      $(containerSelector).not($container).removeClass(activeClass);
    });
    
    // или, к чёрту jquery
    document.addEventListener('click', ({ target: t }) => {
      const container = t.closest(containerSelector);
      const button = t.closest(buttonSelector);
    
      if (button) {
        container.classList.toggle(activeClass);
      }
    
      document.querySelectorAll(containerSelector).forEach(n => {
        if (n !== container) {
          n.classList.remove(activeClass);
        }
      });
    });
    Ответ написан
    3 комментария
  • Как скрыть элементы между элементами с определённым классом?

    0xD34F
    @0xD34F Куратор тега JavaScript
    По кому кликаем: const itemSelector = '.item-service';.

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

    function toggleUntilNextItem(el) {
      while ((el = el.nextElementSibling) && !el.matches(itemSelector)) {
        el.hidden ^= 1;
        // или (в стили надо будет добавить .hidden { display: none; })
        el.classList.toggle('hidden');
      }
    }

    Клики слушать можно непосредственно на интересующих нас элементах:

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

    Или на каком-нибудь из их общих предков:

    document.addEventListener('click', ({ target: t }) =>
      (t = t.closest(itemSelector)) && toggleUntilNextItem(t)
    );
    Ответ написан
    1 комментарий
  • Как взять values из объекта?

    0xD34F
    @0xD34F Куратор тега JavaScript
    Object.entries(obj).filter(n => Number.isInteger(+n[0])).map(n => n[1]).join(';')
    
    // или
    
    Object.keys(obj).reduce((acc, n) => (`${+n}` === n && acc.push(obj[n]), acc), []).join(';')
    
    // или
    
    Array.from(obj).join(';')
    
    // или
    
    JSON.stringify(obj).match(/(?<="\d+":").*?(?=")/g).join(';')
    Ответ написан
    Комментировать
  • Как поменять src картинки с помощью скрипта?

    0xD34F
    @0xD34F Куратор тега JavaScript
    $('.links').on('mouseover', 'a', function() {
      $(this).closest('.block').find('img').attr('src', $(this).data('src'));
    });

    или

    document.addEventListener('mouseover', ({ target: t }) => {
      const { src } = t.dataset;
      if (src) {
        t.closest('.block').querySelector('img').src = src;
      }
    });

    или

    document.querySelectorAll('[data-src]').forEach(function(n) {
      n.addEventListener('mouseenter', this);
    }, ({ target: t }) => t
      .parentNode
      .parentNode
      .nextElementSibling
      .setAttribute('src', t.getAttribute('data-src'))
    );
    Ответ написан
    2 комментария
  • Как связать элементы с помощью data-атрибута?

    0xD34F
    @0xD34F Куратор тега JavaScript
    О каком data-атрибуте идёт речь:

    const key = 'id';
    const attr = `data-${key}`;

    Как из элемента достать значение data-атрибута:

    const getVal = el => $(el).data(key);
    // или
    const getVal = el => $(el).attr(attr);
    // или
    const getVal = el => el.dataset[key];
    // или
    const getVal = el => el.getAttribute(attr);
    // или
    const getVal = el => el.attributes[attr].value;

    По кому кликаем, кому и какой класс надо переключать:

    const buttonSelector = '.open-category';
    const contentSelector = button => `.d-none[${attr}="${getVal(button)}"]`;
    const activeClass = 'active';

    Как переключить класс:

    const toggle = button => $(contentSelector(button)).toggleClass(activeClass);
    
    // или
    
    const toggle = button => document
      .querySelector(contentSelector(button))
      .classList
      .toggle(activeClass);

    Клики можно слушать непосредственно на кнопках:

    $(buttonSelector).click(function() {
      toggle(this);
    });
    
    // или
    
    document.querySelectorAll(buttonSelector).forEach(function(n) {
      n.addEventListener('click', this);
    }, e => toggle(e.currentTarget));

    Или на ком-нибудь из их общих предков:

    $(document).on('click', buttonSelector, e => toggle(e.currentTarget));
    
    // или
    
    document.addEventListener('click', ({ target: t }) =>
      (t = t.closest(buttonSelector)) && toggle(t)
    );
    Ответ написан
    6 комментариев
  • Как удалить элементы массива внутри цикла JS?

    0xD34F
    @0xD34F Куратор тега JavaScript
    идет смещение индексов, но как это обойти, уже не знаю

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

    for (let i = arr.length; i--;) {
      invItems.splice(arr[i], 1);
    }

    Или развернуть его:

    for (const n of [...arr].reverse()) {
      invItems.splice(n, 1);
    }

    Или исправлять индексы - вычитать из них количество уже удалённых элементов, это будет индекс индекса:

    for (const [ i, n ] of arr.entries()) {
      invItems.splice(n - i, 1);
    }

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

    invItems.splice(
      0,
      invItems.length,
      ...invItems.filter((n, i) => !arr.includes(i))
    );

    Или, долой splice, будем использовать два счётчика - один для массива данных, другой для массива индексов. Второй одновременно будет счётчиком удаляемых элементов. Перебираем массив данных, копируем элементы влево на столько позиций, сколько в данный момент элементов должно быть удалено, увеличиваем счётчик удалённых элементов если текущий индекс массива с данными равен текущему элементу массива индексов, после обхода массива с данными удаляем посчитанное количество элементов с конца:

    invItems.length -=
    invItems.reduce((acc, n, i, a) => (
      a[i - acc] = n,
      acc + (i === arr[acc])
    ), 0);
    Ответ написан
    1 комментарий
  • Как сделать рекурсивный последовательный async-await?

    0xD34F
    @0xD34F Куратор тега JavaScript
    Последовательно:

    async function fetchRecursive(arr) {
      const result = [];
    
      if (Array.isArray(arr)) {
        for (const n of arr) {
          try {
            result.push({ status: true, result: await fetch(n.url).then(r => r.json()) });
          } catch(error) {
            result.push({ status: false, error });
          }
    
          result.push(...await fetchRecursive(n.children));
        }
      }
    
      return result;
    }

    Параллельно:

    const flat = arr =>
      (arr || []).flatMap(n => [ n.url, ...flat(n.children) ]);
    
    const fetchRecursive = arr =>
      Promise.allSettled(flat(arr).map(n => fetch(n).then(r => r.json())));
    Ответ написан
    1 комментарий
  • Как идентифицировать каждый блок в jQuery?

    0xD34F
    @0xD34F Куратор тега JavaScript
    Уберите уникальные id, добавьте общие классы.
    Если id нужен, положите его в data-атрибут корневого элемента комментария.
    Уберите атрибут style, его содержимое разместите в стилях класса соответствующего элемента.
    Вместо переключения двух классов у разных элементов переключайте один - у корневого элемента комментария.

    <div class="comment" data-id="187">
      <a class="comment-edit">Редактировать</a>
      <span class="comment-panel"></span>
    </div>

    .comment .comment-panel {
      display: none;
    }
    
    .comment.editing .comment-panel {
      display: inline;
    }
    
    .comment.editing .comment-edit {
      color: red;
    }

    const containerSelector = '.comment';
    const buttonSelector = '.comment-edit';
    const activeClass = 'editing';
    
    
    // вот вам ваш jquery
    $(containerSelector).on('click', buttonSelector, e => {
      const id = $(e.delegateTarget).toggleClass(activeClass).data('id');
      console.log(id);
    });
    
    // или, к чёрту jquery
    document
      .querySelectorAll(`${containerSelector} ${buttonSelector}`)
      .forEach(n => n.addEventListener('click', onClick));
    
    function onClick() {
      const container = this.closest(containerSelector);
      container.classList.toggle(activeClass);
      const { id } = container.dataset;
      console.log(id);
    }
    Ответ написан
    9 комментариев
  • Как вывести атрибут?

    0xD34F
    @0xD34F Куратор тега JavaScript
    Куда и кого надо добавить, о каком data-атрибуте идёт речь:

    const containerSelector = '.class-1';
    const tag = 'div';
    const key = 'name';
    const attrSelector = `[data-${key}]`;

    Добавляем:

    $(containerSelector).prepend(function() {
      return $(`<${tag}>`).text($(attrSelector, this).data(key));
    });

    или

    document.querySelectorAll(containerSelector).forEach(n => {
      const val = n.querySelector(attrSelector).dataset[key];
    
      // можем добавлять разметку
      n.insertAdjacentHTML('afterbegin', `<${tag}>${val}</${tag}>`);
    
      // или элемент
      const el = document.createElement(tag);
      el.textContent = val;
    
      n.prepend(el);
      // или
      // n.firstChild.before(el);
      // n.insertBefore(el, n.firstChild);
      // n.insertAdjacentElement('afterbegin', el);
      // n.firstChild.replaceWith(el, n.firstChild);
    });
    Ответ написан
    1 комментарий