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

    0xD34F
    @0xD34F Куратор тега JavaScript
    Куда кликаем и что фильтруем:

    // такого у вас сейчас нет, сами догадайтесь, кому надо класс добавить
    const containerSelector = '.container';
    
    const buttonsSelector = `${containerSelector} .filter__navigation`;
    const buttonSelector = `${buttonsSelector} [data-filter]`;
    const buttonActiveClass = '_active';
    
    const itemsSelector = `${containerSelector} .search__body`;
    const itemSelector = `${itemsSelector} .filter-column`;
    const itemHiddenClass = '_hide';
    const itemFilterClassPrefix = 'filter__column_';

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

    document.querySelectorAll(buttonSelector).forEach(n => {
      n.addEventListener('click', onFilterButtonClick);
    });
    
    function onFilterButtonClick({ currentTarget: { dataset: { filter } } }) {
      const activeItemClass = itemFilterClassPrefix + filter;
    
      this.closest(buttonsSelector).querySelectorAll(buttonSelector).forEach(n => {
        n.classList.toggle(buttonActiveClass, n === this);
      });
    
      this.closest(containerSelector).querySelectorAll(itemSelector).forEach(({ classList: cl }) => {
        cl.toggle(itemHiddenClass, filter !== 'all' && !cl.contains(activeItemClass));
      })
    }

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

    document.addEventListener('click', e => {
      const button = e.target.closest(buttonSelector);
      if (button) {
        const { filter } = button.dataset;
        const activeItemSelector = filter === 'all' ? '*' : `.${itemFilterClassPrefix}${filter}`;
    
        for (const n of button.closest(buttonsSelector).children) {
          n.classList.toggle(buttonActiveClass, n === button);
        }
    
        for (const n of button.closest(containerSelector).querySelector(itemsSelector).children) {
          n.classList.toggle(itemHiddenClass, !n.matches(activeItemSelector));
        }
      }
    });
    Ответ написан
    Комментировать
  • Как изменить набор ключей в массиве?

    0xD34F
    @0xD34F Куратор тега JavaScript
    Кого на что меняем, объект вида { 'старый ключ': 'новый ключ' }:

    const keys = {
      x: 'a',
      y: 'b',
      z: 'c',
    };

    Собираем новый массив:

    const renameKeys = (obj, keys) =>
      Object.fromEntries(Object
        .entries(obj)
        .map(([ k, v ]) => [ Object.hasOwn(keys, k) ? keys[k] : k, v ])
      );
    
    // или
    
    const renameKeys = (obj, keys) =>
      Object.keys(obj).reduce((acc, k) => (
        acc[keys[k] ?? k] = obj[k],
        acc
      ), {});
    
    
    const newArr = arr.map(n => renameKeys(n, keys));

    Обновляем существующий:

    function renameKeys(keys, obj) {
      for (const k in keys) {
        if (obj.hasOwnProperty(k)) {
          obj[keys[k]] = obj[k];
          delete obj[k];
        }
      }
    }
    
    
    arr.forEach(renameKeys.bind(null, keys));
    Ответ написан
    Комментировать
  • Как эффективно заменить текущий HTML элемент на другой?

    0xD34F
    @0xD34F Куратор тега JavaScript
    элемент.insertAdjacentHTML('afterend', разметка);
    элемент.remove();
    
    // или
    
    элемент.outerHTML = разметка;
    Ответ написан
    4 комментария
  • Как в Apex Charts покрасить маркеры на графике в зависимости от значения?

    0xD34F
    @0xD34F Куратор тега JavaScript
    colors: markerColors, // Устанавливаем цвета маркеров в зависимости от результата

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

    Другой вариант - воспользоваться свойством discrete, позволяющим индивидуально настраивать внешний вид каждого маркера. Всплывающую подсказку это никак не затрагивает, так что её придётся кастомизировать отдельно.
    Ответ написан
    Комментировать
  • Как создать объект с контекстом родительского объекта?

    0xD34F
    @0xD34F Куратор тега JavaScript
    function bindRootContext(obj, context = obj) {
      return new Proxy(obj, {
        get(target, key) {
          const val = target[key];
          return (
            val instanceof Function ? val.bind(Object.hasOwn(target, key) ? context : target) :
            val instanceof Object   ? bindRootContext(val, context) :
                                      val
          );
        },
      });
    }
    
    
    const obj = bindRootContext({
      name: 'Root',
      a: {
        name: 'A',
        logName() { console.log(this.name); },
        b: {
          name: 'B',
          logName() { console.log(this.name); },
          arr: [
            {
              name: '666',
              logName() { console.log(this.name); },
            },
            function() { console.log(this.name); },
          ],
          c: {
            name: 'C',
            logName() { console.log(this.name); },
          },
        },
      },
    });
    
    obj.a.b.c.logName(); // Root
    obj.a.b.logName(); // Root
    obj.a.logName(); // Root
    obj.a.b.arr[0].logName(); // Root
    obj.a.b.arr[1](); // Root
    Ответ написан
    1 комментарий
  • Как вывести на страницу карточки с картинкой, ссылка на которую указываю в модальном окне input?

    0xD34F
    @0xD34F Куратор тега JavaScript
    кнопка.addEventListener('click', () => {
      const [ li ] = шаблон.content.cloneNode(true).children;
      li.querySelector('.card__image').src = инпут_с_ссылкой.value;
      li.querySelector('.card__title').textContent = инпут_с_подписью.value;
      список.insertAdjacentElement('afterbegin', li);
    
      // или
    
      список.prepend(document.importNode(шаблон.content, true));
      список.querySelector('img').setAttribute('src', инпут_с_ссылкой.value);
      список.querySelector('h2').innerText = инпут_с_подписью.value;
    });
    Ответ написан
  • Как добавить событие фокуса на динамические элементы?

    0xD34F
    @0xD34F Куратор тега JavaScript
    К чёрту focus, используйте focusin, оно всплывает, в отличие от.
    Ответ написан
    Комментировать
  • Как найти центральную ячейку таблицы?

    0xD34F
    @0xD34F Куратор тега JavaScript
    Как получить строки таблицы:

    // если гарантируется отсутствие thead и tfoot, или их содержимое также должно учитываться
    const { rows } = table;
    
    // если tbody один
    const [ { rows } ] = table.tBodies;
    
    // если tbody несколько
    const rows = Array.prototype.flatMap.call(table.tBodies, n => [...n.rows]);
    // или
    const rows = [].concat(...Array.from(table.tBodies, n => [...n.children]));
    // или
    const rows = table.querySelectorAll('tbody tr');

    Как получить средний элемент:

    const middle = arr => arr[arr.length >> 1];
    // или
    const middle = arr => arr[Math.floor(arr.length / 2)];
    // или
    const middle = arr => arr[Math.round(~-arr.length / 2)];
    // или
    const middle = arr => arr[(arr.length - arr.length % 2) / 2];

    Всё, можно доставать ячейку:

    const cell = middle(middle(rows).cells);
    // или
    const cell = middle([].reduce.call(rows, (acc, n) => (acc.push(...n.children), acc), []));
    // или, без получения строк
    const cell = middle(table.querySelectorAll('tbody td'));
    Ответ написан
    Комментировать
  • Как зациклить эффект печати?

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

    Куда, что и с какой задержкой будем печатать:

    const el = document.querySelector('p');
    const strings = [ 'hello, world!!', 'fuck the world', 'fuck everything' ];
    const delay = 100;

    Печатаем:

    function Typewriter(el, strings, delay) {
      let i = 0;
      let length = 0;
    
      return setInterval(() => {
        if (++length > strings[i].length) {
          i = -~i % strings.length;
          length = 0;
        }
    
        el.textContent = strings[i].slice(0, length);
      }, delay);
    }
    
    
    const intervalId = Typewriter(el, strings, delay);
    // хотим остановить, делаем так: clearInterval(intervalId);

    или

    function Typewriter(el, strings, delay) {
      let timeoutId = null;
    
      (function step(i, length) {
        length = -~length % -~strings[i].length;
        i = (i + !length) % strings.length;
        el.innerText = strings[i].substring(0, length);
        timeoutId = setTimeout(step, delay, i, length);
      })(0, 0);
    
      return () => clearTimeout(timeoutId);
    }
    
    
    const stop = Typewriter(el, strings, delay);
    // хотим остановить, делаем так: stop();
    Ответ написан
    1 комментарий
  • Как при нажатии на кнопку из ul удалить li с максимальным значением?

    0xD34F
    @0xD34F Куратор тега JavaScript
    При повторном нажатии на кнопку максимальным числом выскакивает число удалённого li.

    Как и должно быть. Кто вам обещал, что вызов метода remove приведёт к исчезновению элемента из lis? Искать максимальный элемент следует среди тех, что всё ещё присутствуют на странице, а не тех, что были изначально - то есть, или надо получать элементы непосредственно перед поиском, или коллекция элементов должна быть динамической.

    Сам поиск максимального элемента - это какая-то шизофрения, какого чёрта вы удаляете элементы до того, как будут проверены значения всех (т.е., до того, как убедитесь, что тот, который собираетесь удалить, действительно максимальный - попробуйте сделать максимальный не первым, и посмотрите, что получится)? При переборе коллекции надо запоминать максимальный элемент, сравнивать с текущим, если текущий больше, обновлять максимум.

    function max(data, key = n => n) {
      const getVal = key instanceof Function ? key : n => n[key];
      let result = null;
    
      for (const n of data) {
        const val = getVal(n);
        if (!result || result[1] < val) {
          result = [ n, val ];
        }
      }
    
      return result?.[0];
    }
    
    
    document.querySelector('button').addEventListener('click', function(e) {
      max(this, n => +n.textContent)?.remove();
      e.target.disabled = !this.length;
    }.bind(document.querySelector('ul').children));
    Ответ написан
  • Как сделать прогресс бар для опросника с разными ответами в зависимости от вопроса и его ответа на alpine js?

    0xD34F
    @0xD34F Куратор тега JavaScript
    Добавить счётчик полученных ответов, делать ему +/- 1 при переходах к следующему/предыдущему вопросу.

    При расчёте прогресса проверять, что текущий вопрос последний (т.е., список возможных следующих вопросов пуст), в зависимости от результата проверки возвращать чего там вам надо - сразу 100 или умножаете счётчик ответов на 10 (кстати, уверены, что больше девяти вопросов задано не будет?).

    UPD. Как избавиться от этой ерунды с умножением на 10 - давайте будем вычислять максимальное количество вопросов, которые ещё могут быть заданы. Тогда текущий прогресс будет выражаться отношением количества отвеченных вопросов к сумме отвеченных и оставшихся.

    А ещё давайте избавимся от поиска вопросов по их id - для этого следующие/предыдущие вопросы надо хранить в виде индексов вместо id, или вместо массива вопросов сделать объект, где ключами будут id.

    Как это может выглядеть:

    Рекурсия есть:

    getMaxQuestionsLeft(question, depth) {
      return Math.max(
        depth,
        ...question.nextQuestions.map(n => {
          return this.getMaxQuestionsLeft(this.questions[n], depth + 1);
        })
      );
    },
    get progress() {
      const ac = this.answersCount;
      return Math.floor(ac / (ac + this.getMaxQuestionsLeft(this.question, 0)) * 100);
    },

    Или, рекурсии нет:

    get maxQuestionsLeft() {
      let result = 0;
    
      for (const stack = [ [ this.question, 0 ] ]; stack.length;) {
        const [ q, d ] = stack.pop();
        result = Math.max(result, d);
        stack.push(...q.nextQuestions.map(n => [ this.questions[n], -~d ]));
      }
    
      return result;
    },
    get progress() {
      const { answersCount: ac } = this;
      return ac / (ac + this.maxQuestionsLeft) * 100 | 0;
    },
    Ответ написан
    2 комментария
  • Почему не получается отфильтровать массив строк?

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

    • Прочитать сообщение об ошибке и попытаться осознать, что в нём сказано
    • Посмотреть, какие значения принимает key при переборе p
    • Открыть документацию и разобраться, что делает цикл for ... in
    • Погуглить, какие ещё бывают способы перебора
    Ответ написан
    Комментировать
  • Как преобразовать 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)/)
    Ответ написан
    Комментировать