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

    0xD34F
    @0xD34F Куратор тега JavaScript
    const getWeekdaysOfMonth = (year, month) =>
      Array.from(
        { length: new Date(year, month, 0).getDate() },
        (_, i) => new Date(year, ~-month, -~i)
          .toLocaleString('ru-RU', { day: 'numeric', weekday: 'short' })
      );
    Ответ написан
    Комментировать
  • Почему в консоли выводится 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
    function parseDate(str) {
      const months = Array.from(
        { length: 12 },
        (_, i) => new Date(0, i).toLocaleString('ru-RU', { month: 'long' })
      );
    
      const [ , month, day, year, hour, minute ] = str.match(/(\S+) (\d+), (\d+) (\d+):(\d+)/);
    
      return +new Date(
        year,
        months.indexOf(month.toLowerCase()),
        day,
        hour,
        minute
      );
    }
    Ответ написан
    1 комментарий
  • Как сделать такой фильтр опций на js или jq?

    0xD34F
    @0xD34F Куратор тега JavaScript
    Собираем селектор на основе состояний чекбоксов (выставлен - класс должен присутствовать, не выставлен - помещаем класс внутрь :not), проверяем фильтруемые элементы на соответствие селектору:

    document.querySelector('.options').addEventListener('change', e => {
      const [ not, has ] = Array.prototype.reduce.call(
        e.currentTarget.querySelectorAll('input'),
        (acc, n) => (acc[+n.checked].push(`.${n.value}`), acc),
        [ [], [] ]
      );
    
      const selector = `${has.join('')}:not(${not.join(',')})`;
    
      for (const n of document.querySelector('.box').children) {
        n.classList.toggle('hidden', !n.matches(selector));
      }
    });

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

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

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

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

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

    0xD34F
    @0xD34F Куратор тега JavaScript
    .dropdown.show {
      display: block;
    }

    const accordionItemSelector = '.data-accordion--summary-container';
    const contentSelector = '.dropdown';
    const activeClass = 'show';
    
    document.body.addEventListener('click', ({ target: t }) => {
      if (t.matches(accordionItemSelector)) {
        document.querySelectorAll(contentSelector).forEach(n => {
          if (!n.contains(t) && !t.contains(n)) {
            n.classList.remove(activeClass);
          };
        });
    
        t.querySelector(contentSelector).classList.toggle(activeClass);
      }
    });
    Ответ написан
    Комментировать
  • Как из 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 newArr2 = arr2.map(n => ({ ...obj1[n.name], ...n }));

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

    arr2.forEach(n => {
      const obj = obj1[n.name];
      if (obj) {
        n.id = obj.id;
      }
    });

    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);
    }
    Ответ написан
    Комментировать
  • Как с помощью reduce и рекурсии получить уровни вложенности для всех вложенных объектов?

    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;
    Ответ написан
    Комментировать
  • Как конвертировать объект в строку из примера?

    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 getSeconds = str =>
      [
        [ 'де?н', 24 * 60 * 60 ],
        [  'час',      60 * 60 ],
        [  'мин',           60 ],
        [  'сек',            1 ],
      ].reduce((acc, n) => {
        return acc + (str.match(RegExp(`\\d+(?=\\s+${n[0]})`)) ?? 0) * 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
    Ответ написан
    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(',')})`;
    }
    Ответ написан
    Комментировать
  • Как отнимать единицу при клике на неверный вариант в квизе?

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

    const questionEl = document.querySelector('ul');
    const resultsEl = document.querySelector('p span');
    
    questionEl.addEventListener('change', showResults);
    
    function showResults() {
      resultsEl.innerText = Array.prototype.reduce.call(
        questionEl.querySelectorAll('input[type="radio"]:checked'),
        (acc, n) => acc + +n.value,
        0
      );
    }

    А вообще, правильно было бы показывать результат только после получения всех ответов; вопросы показывать по одному; не зашивать в разметку вопросы и варианты ответов. Как-то так.
    Ответ написан
    Комментировать