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

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

    Как видите, всё довольно просто.
    Ответ написан
    Комментировать
  • Как получить массив дней месяца, с пн по вс?

    0xD34F
    @0xD34F Куратор тега JavaScript
    const getWeekdaysOfMonth = (year, month) =>
      Array.from(
        { length: new Date(year, month, 0).getDate() },
        function() {
          this[0].setDate(-~this[0].getDate());
          this[1] += this[0].getDay() === 1 || !this[1];
          return `неделя ${this[1]}, ` + this[0].toLocaleString('ru-RU', {
            day: 'numeric',
            weekday: 'short',
          });
        },
        [ new Date(year, ~-month, 0), 0 ]
      );

    const may2024 = getWeekdaysOfMonth(2024, 5);
    const sep2023 = getWeekdaysOfMonth(2024, -3);
    const jun2021 = getWeekdaysOfMonth(2020, 18);
    Ответ написан
    Комментировать
  • Почему в консоли выводится 6 и 340?

    0xD34F
    @0xD34F Куратор тега JavaScript
    .then(
      (x) => x + 1,
      (x) => x + 3
    ) //promise rejected, value = 14

    Так, а второй коллбек then'а - он для чего нужен? Тройка добавится, будет 17. И, соответственно, последующие catch'и пропускаются, будет выполнен .then((x) => x * 20).
    Ответ написан
    1 комментарий
  • Почему кнопка постоянно disabled?

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

    Сколько у вас чекбоксов?
    Что выдаёт метод every для пустого массива?
    Как работает оператор ||?
    Ответ написан
    2 комментария
  • Стоит ли использовать lodash сейчас?

    0xD34F
    @0xD34F Куратор тега JavaScript
    Я так понимаю все что предлагает лодаш уже реализовано нативно

    Не понимаете.
    Ответ написан
    Комментировать
  • Как перевести дату в строке в формат UNIX?

    0xD34F
    @0xD34F Куратор тега JavaScript
    const months = Object.fromEntries(Array.from(
      { length: 12 },
      (_, i) => [ new Date(0, i).toLocaleString('ru-RU', { month: 'long' }), i ]
    ));
    
    function parseDate(str) {
      const [ , month, day, year, hour, minute ] = str.match(/(\S+) (\d+), (\d+) (\d+):(\d+)/);
      return +new Date(year, months[month.toLowerCase()], day, hour, minute);
    }
    Ответ написан
    1 комментарий
  • Как сделать такой фильтр опций на js или jq?

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

    const checkboxContainer = document.querySelector('.options');
    const itemsContainer = document.querySelector('.box');
    const hiddenClass = 'hidden';

    Собираем селектор на основе состояний чекбоксов (выставлен - класс должен присутствовать, не выставлен - помещаем класс внутрь :not):

    function getCheckedSelector(container) {
      const [ not, has ] = Array.prototype.reduce.call(
        container.querySelectorAll('input'),
        (acc, n) => (acc[+n.checked].push(`.${n.value}`), acc),
        [ [], [] ]
      );
    
      return has.join('') + (not.length ? `:not(${not.join(',')})` : '');
    }

    Затем проверяем фильтруемые элементы на соответствие селектору:

    checkboxContainer.addEventListener('change', function() {
      const selector = getCheckedSelector(this);
    
      for (const n of itemsContainer.children) {
        n.classList.toggle(hiddenClass, !n.matches(selector));
      }
    });

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

    const toggleHidden = (selector, state) => itemsContainer
      .querySelectorAll(selector)
      .forEach(n => n.classList.toggle(hiddenClass, state));
    
    checkboxContainer.addEventListener('change', e => {
      toggleHidden(':scope > *', true);
      toggleHidden(getCheckedSelector(e.currentTarget), false);
    });

    ИЛИ

    При обработке элемента перебираем чекбоксы и проверяем, что их состояния соответствуют наличию классов у данного элемента:

    const checkboxes = [...checkboxContainer.querySelectorAll('input')];
    const onChange = function() {
      this.forEach(({ classList: cl }) => {
        cl.toggle(hiddenClass, checkboxes.some(n => n.checked !== cl.contains(n.value)));
      });
    }.bind([...itemsContainer.children]);
    
    checkboxes.forEach(n => n.addEventListener('change', onChange));
    Ответ написан
  • Как разбить многостроковый текст на массив с помощью регулярного выражения?

    0xD34F
    @0xD34F Куратор тега JavaScript
    Понимаю что можно сделать проще с помощью split, но хочу научиться именно с помощью регулярного выражения

    Одно другому не мешает:

    str.split(/\n(?=#EXTINF)/)
    Ответ написан
    Комментировать
  • Как кликать на вложенные элементы так, чтобы не кликался родительский элемент?

    0xD34F
    @0xD34F Куратор тега JavaScript
    .data-accordion--summary-container.active > .dropdown {
      display: block;
    }

    const itemSelector = '.data-accordion--summary-container';
    const activeClass = 'active';
    const toggle = el => el
      .parentNode
      .querySelectorAll(`:scope > ${itemSelector}`)
      .forEach(n => n.classList[n === el ? 'toggle' : 'remove'](activeClass));

    // применяем делегирование
    document.body.addEventListener('click', ({ target: t }) =>
      t.matches(itemSelector) && toggle(t)
    );
    
    // или, назначаем обработчик клика каждому элементу индивидуально
    document.querySelectorAll(itemSelector).forEach(function(n) {
      n.addEventListener('click', this);
    }, e => e.currentTarget === e.target && toggle(e.target));
    Ответ написан
    Комментировать
  • Как из query string получить объект следующего вида?

    0xD34F
    @0xD34F Куратор тега JavaScript
    Просто исправить существующий код:

    - acc[key] = [value];
    + (acc[key] ??= []).push(value);

    Но можно его ещё и сократить:

    const convertQueryStringToObject = str => Array
      .from(new URLSearchParams(str))
      .reduce((acc, n) => ((acc[n[0]] ??= []).push(n[1]), acc), {});

    Или неоправданно усложнить и изуродовать:

    const convertQueryStringToObject = str => Array
      .from(str.matchAll(/([^&]+)=([^&]+)/g))
      .reduce((acc, [ , k, v ]) => (
        Object.hasOwn(acc, k) || (acc[k] = []),
        acc[k][acc[k].length] = v,
        acc
      ), {});
    Ответ написан
    1 комментарий
  • Как высчитать уровень исходя из массива?

    0xD34F
    @0xD34F Куратор тега JavaScript
    работает, но кажется, что достаточно криво

    Да, криво - всегда перебирается весь массив.

    Можно идти от конца к началу до тех пор, пока не встретится подходящий элемент:

    let lvl = 0;
    
    for (let i = LVLS.length; i--;) {
      if (LVLS[i].exp <= EXP) {
        lvl = LVLS[i].lv;
        break;
      }
    }

    переделать это во что-то более красивое

    const lvl = LVLS.findLast(n => n.exp <= EXP)?.lv ?? 0;

    А вообще, учитывая, что уровни представлены натуральными числами без пропусков, отдельные свойства под их значения не нужны, можно искать индекс:

    const LVLS = [ 10, 25, 45, 70, 100 ];
    
    const lvl = 1 + LVLS.findLastIndex(n => n <= EXP);
    Ответ написан
    Комментировать
  • Как отобразить данные по тегам из json на странице?

    0xD34F
    @0xD34F Куратор тега JavaScript
    <button data-country="">Показать всё</button>
    <button data-country="France">Франция</button>
    <button data-country="Germany">Германия</button>
    <button data-country="England">Англия</button>

    const catalogEl = document.querySelector('#catalog-box');
    let catalogArr = [];
    
    const buttons = document.querySelectorAll('button[data-country]');
    buttons.forEach(n => n.addEventListener('click', onClick));
    
    function onClick({ target: t }) {
      buttons.forEach(n => n.classList.toggle('active', n === t));
      renderCatalog(t.dataset.country);
    }
    
    function renderCatalog(country) {
      const toRender = country
        ? catalogArr.filter(n => n.tag === country)
        : catalogArr;
    
      catalogEl.innerHTML = toRender
        .map(n => `
          <div class="catalog__card" id="${n.id}">
            <img src="${n.img}" alt="${n.alt}" class="catalog__card_img">
            <p class="catalog__card_author">${n.author}</p>
            <p class="catalog__card_name">${n.name}</p>
            <p class="catalog__card_note">${n.note}</p>
            <p class="catalog__card_price">${n.price}</p>
            <button class="catalog__card_btn">${n.btn}</button>
          </div>`)
        .join('');
    }
    
    fetch('catalogBox.json')
      .then(r => r.json())
      .then(r => {
        catalogArr = r;
        buttons[0].click();
      });
    Ответ написан
    Комментировать
  • Как сформировать массив объектов исходя из 2-х других массивов объектов?

    0xD34F
    @0xD34F Куратор тега JavaScript
    Сначала из первого массива сделаем объект, где значениями будут его элементы, а ключами - значения их, элементов, свойства name:

    const obj1 = Object.fromEntries(arr1.map(n => [ n.name, n ]));
    
    // или
    
    const obj1 = arr1.reduce((acc, n) => (acc[n.name] = n, acc), {});

    Затем можно собрать новый массив:

    const newArr2 = arr2.map(n => ({ ...obj1[n.name], ...n }));
    
    // или
    
    const newArr2 = [];
    for (let i = 0; i < arr2.length; i++) {
      newArr2.push(Object.assign({}, obj1[arr2[i].name], arr2[i]));
    }

    Или обновить существующий:

    arr2.forEach(n => Object
      .entries(obj1[n.name] ?? {})
      .forEach(([ k, v ]) => Object.hasOwn(n, k) || (n[k] = v))
    );
    
    // или
    
    for (const n of arr2) {
      const obj = obj1[n.name];
      for (const k in obj) {
        if (!n.hasOwnProperty(k)) {
          n[k] = obj[k];
        }
      }
    }

    UPD. Вынесено из комментариев:

    можно ли исключить объекты у которых не изменился value? То есть что бы этих объектов не было в итоговом массиве.

    const newArr2 = arr2.reduce((acc, n) => (
      obj1[n.name]?.value !== n.value && acc.push({ ...obj1[n.name], ...n }),
      acc
    ), []);
    Ответ написан
    5 комментариев
  • Как ограничить количество отмеченных чекбоксов?

    0xD34F
    @0xD34F Куратор тега JavaScript
    Отключаем чекбоксы, изменение состояния которых нежелательно:

    function restrictChecked({
      container,
      selector = 'input[type="checkbox"]',
      min = 0,
      max = Infinity,
      enableOnCancel = true,
    }) {
      const checkboxes = [...container.querySelectorAll(selector)];
      const onChange = () => {
        const countChecked = checkboxes.reduce((acc, n) => acc + n.checked, 0);
        const minReached = countChecked <= min;
        const maxReached = countChecked >= max;
        checkboxes.forEach(n => n.disabled = minReached && n.checked || maxReached && !n.checked);
      };
    
      checkboxes.forEach(n => n.addEventListener('change', onChange));
      onChange();
    
      return () => checkboxes.forEach(n => {
        n.disabled &&= !enableOnCancel;
        n.removeEventListener('change', onChange);
      });
    }

    Или, откатываем нежелательные изменения:

    function restrictChecked({
      container,
      selector = 'input[type="checkbox"]',
      min = 0,
      max = Infinity,
    }) {
      function onChange({ target: t }) {
        if (t.matches(selector)) {
          const countChecked = this.querySelectorAll(`${selector}:checked`).length;
          t.checked ||= countChecked <  min;
          t.checked &&= countChecked <= max;
        }
      }
    
      container.addEventListener('change', onChange);
    
      return () => container.removeEventListener('change', onChange);
    }
    Ответ написан
    Комментировать
  • Как получить уровни вложенности для всех вложенных объектов?

    0xD34F
    @0xD34F Куратор тега JavaScript
    Функции добавить параметр - глубину вложенности.

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

    const addDepth = (val, depth = 0) =>
      val instanceof Object
        ? Object.entries(val).reduce((acc, n) => (
            acc[n[0]] = addDepth(n[1], depth + 1),
            acc
          ), { depth })
        : val;

    Но вот вопрос - что если в каких-то объектах уже присутствует свойство с тем же именем, под которым записывается глубина? Как избежать коллизии имён? Можно в качестве имени свойства для глубины использовать символ (на этот раз без reduce'а, без рекурсии, да и копия объекта не создаётся):

    const depthKey = Symbol();
    
    function addDepth(val) {
      for (const stack = [ [ val, 0 ] ]; stack.length;) {
        const [ n, depth ] = stack.pop();
        if (n instanceof Object) {
          n[depthKey] = depth;
          stack.push(...Object.values(n).map(m => [ m, -~depth ]));
        }
      }
    }
    Ответ написан
    Комментировать
  • Как конвертировать объект в строку из примера?

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

    const toString = (val, keys = []) =>
      val instanceof Object
        ? Object.entries(val).map(([ k, v ]) => {
            keys.push(k);
            const result = toString(v, keys);
            keys.pop();
            return result;
          }).join('&')
        : `${keys.join('.')}=${val}`;

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

    function toString(val) {
      const result = [];
    
      for (const stack = [ [ val, [] ] ]; stack.length;) {
        const [ n, keys ] = stack.pop();
        if (n instanceof Object) {
          stack.push(...Object.entries(n).map(([ k, v ]) => [ v, [ ...keys, k ] ]).reverse());
        } else {
          result.push(`${keys.join('.')}=${n}`);
        }
      }
    
      return result.join('&');
    }
    Ответ написан
    1 комментарий
  • Как строку с часами, минутами и секундами преобразовать в число?

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

    const units = [
      [ 'де?н', 24 * 60 * 60 ],
      [  'час',      60 * 60 ],
      [  'мин',           60 ],
      [  'сек',            1 ],
    ].map(n => [ RegExp(`\\d+(?=\\s+${n[0]})`), n[1] ]);
    
    const getSeconds = str =>
      units.reduce((acc, n) => acc + n[0].exec(str) * n[1], 0);

    getSeconds('2 часа 22 секунды') // 7222
    getSeconds('99 минут') // 5940
    getSeconds('1 час 1 минута 1 секунда') // 3661
    getSeconds('1 день 23 часа 59 минут 60 секунд') // 172800
    getSeconds('2 дня') // 172800
    getSeconds('546 секунд и ещё 2 минуты') // 666

    Если вдруг возможно повторение единиц измерения внутри одной строки, то поиск с помощью регулярных выражений должен быть глобальным - RegExp(`\\d+(?=\\s+${n[0]})`, 'g'), а функция подсчёта секунд примет следующий вид:

    const getSeconds = str =>
      units.reduce((seconds, [ reg, multiplier ]) => {
        return [...str.matchAll(reg)].reduce((acc, n) => acc + n * multiplier, seconds);
      }, 0);
    
    // или
    
    const getSeconds = str => units
      .flatMap(n => (str.match(n[0]) ?? []).map(m => m * n[1]))
      .reduce((acc, n) => acc + n, 0);
    
    // или
    
    const getSeconds = str => eval(units
      .map(n => `${n[1]} * (${str.match(n[0])?.join('+') ?? 0})`)
      .join('+')
    );

    getSeconds('1 секунда плюс 3 секунды плюс 5 секунд') // 9
    getSeconds('21 день, 7 дней, да ещё 3 дня - всего секунд в мае месяце будет') // 2678400
    Ответ написан
    2 комментария
  • Как сделать развертывание аккордеона плавным?

    0xD34F
    @0xD34F Куратор тега CSS
    .panel {
      padding: 0 15px;
      transition: all 0.4s;
      height: 0;
      overflow: hidden;
    }
    
    .faq-item.active .panel {
      padding: 15px 15px 20px;
    }
    
    .faq-item.active .cross {
      transform: rotate(45deg);
    }

    const containerSelector = '.faq-list';
    const itemSelector = '.faq-item';
    const headerSelector = '.accordion';
    const contentSelector = '.panel';
    const activeClass = 'active';
    const toggle = item => item
      ?.closest(containerSelector)
      ?.querySelectorAll(itemSelector).forEach(n => {
        const state = n === item && !n.classList.contains(activeClass);
        const content = n.querySelector(contentSelector);
        n.classList.toggle(activeClass, state);
        content.style.height = `${state ? content.scrollHeight : 0}px`;
      });

    document.addEventListener('click', e => {
      toggle(e.target.closest(headerSelector)?.closest(itemSelector));
    });
    
    // или
    
    document.querySelectorAll(headerSelector).forEach(n => {
      n.addEventListener('click', toggle.bind(null, n.closest(itemSelector)));
    });
    Ответ написан
    Комментировать
  • Как перемещать элемент по клику на кнопки?

    0xD34F
    @0xD34F Куратор тега JavaScript
    Вместо выдёргивания координат из стилей лучше будет сделать массив с координатами, обновлять этот массив, и уже на его основе задавать translate элементу. А чтобы не делать отдельные обработчики клика всем кнопкам, можно записать им в data-атрибут информацию о том, на сколько какие координаты должна изменить данная кнопка.

    <button data-steps="1,0">right</button>
    <button data-steps="-1,0">left</button>
    <button data-steps="0,1">down</button>
    <button data-steps="0,-1">up</button>
    <button data-steps="1,1">right down</button>
    <button data-steps="0,-2">double up</button>

    const coord = [ 0, 0 ];
    const stepSize = 30;
    
    const box = document.querySelector('#box');
    const buttons = document.querySelectorAll('[data-steps]');
    
    buttons.forEach(n => n.addEventListener('click', onClick));
    
    function onClick() {
      this.dataset.steps.split(',').forEach((n, i) => coord[i] += n * stepSize);
      box.style.transform = `translate(${coord.map(n => `${n}px`).join(',')})`;
    }
    Ответ написан
    Комментировать