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

    sergiks
    @sergiks Куратор тега JavaScript
    ♬♬
    Состояние страницы описывается набором закрытых блоков. Понадобится ещё и время их закрытия, чтобы игнорировать устаревшие. Предлагаю хранить объект, где ключи – id блоков. А значения – timestamp когда их закрыли. Что-то типа
    const boxState = {
      "block-ad-123": 1668436835032,
      "block-ad-456": 1668436842279,
    };
    Хранить этот объект можно в localStorage, переведя в строку: JSON.stringify(boxState)

    Изначально закрытых блоков нет, просто пустой объект.

    Алгоритм
    Загрузили страницу, все блоки видны.
    Считали состояние из localStorage, и если там пусто, взяли пустой объект.
    Пройти по объекту, и если прошло еще мало времени по очередному ключу, скрыть его блок.
    Если времени прошло много – удалить этот ключ из стейта.
    Если при обходе объекта были удаления – сохранить обновлённый стейт в localStorage.
    spoiler
    const boxKey = 'BOX_STATE'; // ключ для хранения в localStorage
    const boxState = JSON.parse(localStorage.getItem(boxKey)) ?? {};
    
    const TTL = 36e5; // сколько держать блок скрытым, в миллисекундах (1 час)
    const expiredIds = [];
    Object.entries(boxState)
      .forEach((key, value) => {
        if (value + TTL < Date.now()) {
          // устарел, более не скрываем
          expiredIds.push(key);
        } else {
          // актуален, скрываем блок
          document.getElementById(key).classList.add("hide");
        }
      });
    
      if (expiredIds.length) {
        expiredIds.forEach(id => delete boxState[id]);
        localStorage.setItem(boxKey, JSON.stringify(boxState));
      }


    При нажатии на крестик:
    1. добавить в стейт id этого блока и текущее время Date.now(),
    2. сохранить состояние в localStorage,
    3. добавить класс блоку.
    spoiler
    const onClose = ({ target }) => {
      const block = target.closest('div.block');
      const { id } = block;
      boxState[id] = Date.now();
      localStorage.setItem(boxKey, JSON.stringify(boxState));
      block.classList.add("hide");
    };
    document.querySelectorAll('img.x-button')
      .addEventListener('click', onClose);
    Ответ написан
    Комментировать
  • Код должен выводить текущий месяц, но почему-то не выводит, почему?

    sergiks
    @sergiks Куратор тега JavaScript
    ♬♬
    Всё, что связано с датами в JS – в объекте Date.
    Из него можно вытащить и время и месяц и всё остальное.
    Месяцы в этом объекте обозначаются числом от 0 (январь) до 11 (декабрь).

    Понадобится создать объект Date, отображающий «сейчас: new Date()
    И вытащить из него значение месяца getMonth()

    Чтобы перевести полученное число 0..11 в слово, как вариант, можно составить массив имён месяцев:
    const monthNames = ['январь', 'февраль', ... 'декабрь'];
    Удобно: под индексом 0 тут как раз "январь", под 11 "декабрь".
    Ответ написан
    Комментировать
  • Как объединить две функции в одну?

    sergiks
    @sergiks Куратор тега JavaScript
    ♬♬
    Тут 6 параметров – две группы по 3:
    1. кого слушаем #search_table VS #search_card
    2. что ищем input[name^=name] VS [data-item-name="name"]
    3. и с чем сравниваем val() VS text()


    Вытащить всё в параметры:
    const addFilter = (listenSelector, findSelector, methodName) => {
      $(listenSelector).keyup(function search() {
        const search = this;
        const searchValue = $(search).val().toLowerCase();
    
        $("#tabname tbody tr")
          .find(findSelector)
          .each(function () {
            const currentValue = $(this)[methodName]().toLowerCase();
            $(this).closest("tr").toggle(currentValue.includes(searchValue));
          });
      });
    };
    
    addFilter('#search_table', 'input[name^=name]', 'val');
    addFilter('#search_card', '[data-item-name="name"]', 'text');
    Ответ написан
    7 комментариев
  • Собрать все ошибки error в массив и вернуть промис?

    sergiks
    @sergiks Куратор тега JavaScript
    ♬♬
    Чтобы выполнить все запросы, и после получить массив по итогам, можно использовать метод Promise.allSettled() – передать в него массив промисов запросов Axios, и в следующий then() попадёт массив с результатами:
    const batch = new Array(214)
      .fill()
      .map((_, i) => `https://example.com/api/${i}`) // URL запросов
      .map(url => axios.get(url));
    
    Promise.allSettled(batch)
      .then(axiosResults => {
        const results = [];
        const errors = [];
    
        axiosResults.forEach(({ status, res }) => {
          if (status === 'fullfilled') {
            results.push(res);
          } else if (status === 'rejected') {
            errors.push(res);
          }
        });
    
        return { results, errors };
      })
      .then(({ results, errors }) => {
        console.log('Вот результаты: %o, а вот ошибки: %o', results, errors);
      });
    Ответ написан
  • Как сократить запись if?

    sergiks
    @sergiks Куратор тега JavaScript
    ♬♬
    Хорошо бы в какой-то переменной держать состояние, которое бывает одно из двух: true / false

    let isExpanded = false; // единственный источник правды
    
    el.addEventListener('click', () => {
      isExpanded = !isExpanded; // переключить
    
      // показать
      linkusersnew.innerText = isExpanded ? 'Свернуть' : 'Развернуть';
      userwrap.style.height = isExpanded ? '600px' : '400px';
      newdel.style.display = isExpanded ? 'flex' : 'none';
    });
    Ответ написан
    Комментировать
  • Не работает код Js что делать?

    sergiks
    @sergiks Куратор тега JavaScript
    ♬♬
    Заглядывайте в консоль браузера — сообщения об ошибках бывают весьма полезны.
    - const signinbtn = document.queryselector('.signn-btn');
    - const signupbtn = document.queryselector('.signup-btn');
    - const formbox = document.queryselector('.formbox');
    + const signinbtn = document.querySelector('.signin-btn'); // имя класса
    + const signupbtn = document.querySelector('.signup-btn');
    + const formbox = document.querySelector('.formbox');

    Та же проблема потери заглавных букв в addEventListener и classList.
    Косяки с именами классов кнопок — проверяйте. В разметке одно имя класса, в querySelector() – чуть другое.

    На будущее, пожалуйста, такие простыни кода не пихайте в текст вопроса, а создавайте песочницу с работающим кодом, например, на Codepen.io:
    Ответ написан
    2 комментария
  • Как из массива строк получить те, у которых длина равна 5?

    sergiks
    @sergiks Куратор тега JavaScript
    ♬♬
    Целиком elem не нужен — только его свойство length (длина)

    const arr = ['katya', 'Karolina', 'Vladislav', 'Anna', 'Habr', 'Lepra'];
    console.log(
      arr.filter(({ length }) => length === 5)
    );
    Ответ написан
    Комментировать
  • Как оптимизировать нерекурсивный обход дерева?

    sergiks
    @sergiks Куратор тега JavaScript
    ♬♬
    судя по данным, тут не бинарное дерево, и в свойстве v всегда число ("v" for "value"),
    а в свойстве c — опциональный массив дочерних элементов ("c" for "children").

    Проверка if (last && Array.isArray(last.v)) { тут, видимо, лишняя.
    Хотя самый первый раз как раз v это массив.. Так что нужна, если данные именно такие.

    Как и if (last && typeof last.c === 'object') {, за исключением корневого свойства, где это таки объект.

    По мелочи — все 4 проверки одинаково проверяют наличие last

    Оптимизировать что-то — зачем? Тут лишняя операция, если только сортировка результатов, скрывающая порядок обхода.
    Ответ написан
    Комментировать
  • Как написать функцию, которая находит несколько тегов и заменяет их?

    sergiks
    @sergiks Куратор тега JavaScript
    ♬♬
    Достаточно заменять не на единственный новый селект, а на его клон:
    - el.parentNode.replaceChild(customSelect, el);
    + el.parentNode.replaceChild(customSelect.cloneNode(true), el);


    Ответ написан
    Комментировать
  • Как правильно задать addEventListener?

    sergiks
    @sergiks Куратор тега JavaScript
    ♬♬
    Когда элементы создаются-добавляются динамически, удобно использовать делегирование событий:
    слушать клики не на самих чекбоксах, а выше, на общем родителе, на этом settParent.

    Тогда не нужно вешать слушателя на каждый новый чекбокс.
    settParent.addEventListener('input', ({ target }) => console.log(target.value));
    Ответ написан
    Комментировать
  • Как понять по какому элементу в массиве кликнул пользователь?

    sergiks
    @sergiks Куратор тега JavaScript
    ♬♬
    Пример:

    В константу li попадает кликнутый элемент списка.
    Ответ написан
    Комментировать
  • Возможно ли переписать этот код, чтобы в нём не использовались String и кавычки?

    sergiks
    @sergiks Куратор тега JavaScript
    ♬♬
    export default str => {
      const validLetters = (625442493).toString(36);
      const emptyString = [] + [];
    
      return str
        .split(emptyString)
        .map(c => (validLetters.includes(c) ? c.charCodeAt(0) - 61 : Number(c).toString() === c ? c : emptyString))
        .join(emptyString);
    };
    Ответ написан
    2 комментария
  • Почему выводится вчерашняя дата вместо начала сегодняшнего дня? Как вывести дату начала дня?

    sergiks
    @sergiks Куратор тега JavaScript
    ♬♬
    В исходном примере получаются два объекта даты на начало и конец дня 7-го ноября, всё в порядке.
    Во временной зоне браузера — в данном случае, Москвы UTC+3.
    А выводятся они почему-то во временной зоне UTC.

    Когда в Москве полночь, начало 7-го ноября — по UTC ещё 21 час 6-го.

    Так что вопрос не создания, а вывода полученных дат.

    Вот ещё так можно их создавать, но это ничего
    не изменит
    const dateInterval = [new Date(), new Date()];
    
    const setTimes = interval =>
      interval
        .map(d => new Date(d))
        .map((d, i) => {
          d.setHours(23 * i);
          d.setMinutes(59 * i);
          d.setSeconds(59 * i);
          d.setMilliseconds(999 * i);
          return d;
        });
    
    setTimes(dateInterval)
    // Array [ 
    //  Date Mon Nov 07 2022 00:00:00 GMT+0300 (Moscow Standard Time),
    //  Date Mon Nov 07 2022 23:59:59 GMT+0300 (Moscow Standard Time)
    // ]
    Ответ написан
  • Заменить текст в массив, как это делается?

    sergiks
    @sergiks Куратор тега JavaScript
    ♬♬
    Из формы менять данные в массиве. И заново отрисовывать массив.
    Vue/React оптимизируют этот момент и перерисовывают только изменения.
    Клик можно ловить выше по дереву 1 раз. Так проще будет добавлять новые «статьи» без необходимости вешать на каждую слушатели клика.
    Ответ написан
    Комментировать
  • Как выбрать все элементы, чтобы применить к ним скрипт?

    sergiks
    @sergiks Куратор тега JavaScript
    ♬♬
    Удобно использовать URL API и его URLSearchParams:
    const { searchParams } = new URL(document.location.href);
    const url = new URL('https://ya.ru');
    
    ['utm_source', 'utm_medium', 'utm_campaign']
      .filter(param => searchParams.has(param))
      .forEach(param => url.searchParams.set(param, searchParams.get(param)));
    const urlString = url.toString();
    
    document.querySelectorAll('div.tg-button > a')
      .forEach(el => el.setAttribute('href', urlString));
    Получить объект параметр-значение из адреса текущей страницы;
    создать новый URL для ссылой в кнопки;
    отфильтровать из набора utm-меток те, что есть в адресной строке,
    и скопировать их в ссылку-для-кнопки.

    Потом проставить всем кнопкам эту ссылку.
    Ответ написан
    Комментировать
  • Как выполнить многомерный index?

    sergiks
    @sergiks Куратор тега JavaScript
    ♬♬
    const collection = Array.from(document.querySelectorAll('#pop div.me'));
    // массив с этими DIV'ами, игнорируя вложенность
    
    collection[1] // второй <div class='me'></div>
    Ответ написан
    Комментировать
  • Как перебрать массив в массиве и присвоить каждому элементу значения из другого массива?

    sergiks
    @sergiks Куратор тега JavaScript
    ♬♬
    Удобно использовать Object.assign() — метод копирует все перечисляемые свойства в целевой объект:
    const target = { a: '', b: 'V'}; // скопируем сюда
    const source = { a: 'AAA', c: 'CCC' }; // отсюда
    Object.assign(target, source);
    target // { a: "AAA", b: "V", c: "CCC" }


    Если не беда, что опустошатся participants, то можно так:
    hotel.forEach(({ room }) => room.forEach(slot => Object.assign(slot, participants.pop())));


    Ну или так, с нетронутыми participants.
    let i = 0;
    hotel.forEach(({ room }) =>
      room.forEach((slot) => Object.assign(slot, participants[i++]))
    );
    Ответ написан
    1 комментарий
  • Как покрасить текст до br?

    sergiks
    @sergiks Куратор тега JavaScript
    ♬♬
    Можно посмотреть childNodes этого параграфа. Их будет три: текст, элемент, текст.
    Заменить последний на его клон в обёртке:
    Ответ написан
  • Вопрос про тест на lear js?

    sergiks
    @sergiks Куратор тега JavaScript
    ♬♬
    Этот тест на понимание отличия
    объявления функции
    и функционального выражения.

    function f() {}; // традиционное объявление функции
    тут f вылезет наверх ("hoisting") и будет доступна всему коду.

    (function f() {}) // функциональное выражение function expression
    
    // ещё варианты
    const x = function f() {};
    console.log( function f() {} );
    имя f будет относиться к этой функции только внутри неё, для рекурсивных вызовов, например.

    В тесте if( function() {}) как раз создаёт function expression. Поэтому f нигде снаружи не видна.
    Ответ написан
    Комментировать
  • Можно ли, взяв произвольную точку ширины слайдера input range в пикселях, узнать какое значение value в этой точке?

    sergiks
    @sergiks Куратор тега JavaScript
    ♬♬
    Там же линейная интерполяция:
    value = 1000 + 135 * (15000 - 1000) / 180 = 11500

    Но это не учитывает сокращение диапазона пикселей из-за толщины «рукоятки» слайдера — а это уже зависит от стилей, платформы, браузера, положения Венеры в Марсе.

    Так выглядит range slider в крайнем правом положении в Mozilla Firefox 106 под Mac Os X:
    635fc7bb742cb744029407.pngширина 150px в масштабе 170%. Сверху точно той же ширины div с тёмной заливкой. CodePen.

    Слайдер почему-то смещен на неск. пикселей правее div'а, но длина точно такая же.
    Кнопка ездит, не вылезая за края диапазона. Значит, фактический диапазон пикселей меньше, чем сам слайдер.
    Ответ написан
    3 комментария