Задать вопрос
Ответы пользователя по тегу JavaScript
  • Как переключать классы у 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 комментарий
  • Как создать динамически маркеры на Google Maps API из массива после обработки GeoCoder?

    0xD34F
    @0xD34F Куратор тега JavaScript
    Объявление функции codeAddress - уберите цикл; в саму функцию передавайте адрес в качестве параметра:

    function codeAddress(address, geocoder, map) {
      geocoder.geocode({ address }, function(results, status) {
        if (status === 'OK') {
          new google.maps.Marker({
            map: map,
            position: results[0].geometry.location,
          });
        } else {
          alert('Geocode was not successful for the following reason: ' + status);
        }
      });
    }

    В initMap перебирайте массив адресов, для каждого вызывайте codeAddress:

    address.forEach(n => codeAddress(n, geocoder, map));
    Ответ написан
    4 комментария
  • Как добавить элемент родителю?

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

    const elements = document.querySelectorAll('li > ul');
    const tag = 'span';
    const text = 'hello, world!!';

    Добавляем:

    elements.forEach(n => {
      n.before(document.createElement(tag));
      n.previousSibling.textContent = text;
    });

    или

    for (const n of elements) {
      const el = document.createElement(tag);
      el.innerText = text;
      n.parentNode.insertBefore(el, n);
    }

    или

    for (let i = 0; i < elements.length; i++) {
      elements[i].insertAdjacentHTML('beforebegin', `<${tag}>${text}</${tag}>`);
    }

    или

    (function insert(i, n = elements.item(i)) {
      if (n) {
        const el = document.createElement(tag);
        el.appendChild(new Text(text));
        n.insertAdjacentElement('beforebegin', el);
        insert(-~i);
      }
    })(0);
    Ответ написан
  • Как скрывать элементы при несовпадении data-атрибутов?

    0xD34F
    @0xD34F Куратор тега JavaScript
    О каких элементах и атрибутах идёт речь:

    const selector = '.list-item';
    const key1 = 'pagereview';
    const key2 = 'pageslug';
    const attr1 = `data-${key1}`;
    const attr2 = `data-${key2}`;

    Получаем элементы:

    const $elements = $(selector);
    
    // или
    
    const elements = document.querySelectorAll(selector);

    Переключаем их видимость:

    $elements.show().filter((i, n) => $(n).data(key1) !== $(n).data(key2)).hide();
    
    // или
    
    $elements.each(function() {
      const $this = $(this);
      $this.toggle($this.attr(attr1) === $this.attr(attr2));
    });
    
    // или
    
    elements.forEach(n => {
      n.hidden = n.getAttribute(attr1) !== n.getAttribute(attr2);
    });
    
    // или
    
    for (const { style, dataset } of elements) {
      style.display = dataset[key1] === dataset[key2] ? 'block' : 'none';
    }
    
    // или (в стили надо будет добавить .hidden { display: none; })
    
    for (let i = 0; i < elements.length; i++) {
      const { classList: c, attributes: a } = elements[i];
      c.toggle('hidden', a[attr1].value !== a[attr2].value);
    }
    Ответ написан
    Комментировать
  • Как сделать так чтобы jquery фильтр искал по нескольким значениям?

    0xD34F
    @0xD34F Куратор тега JavaScript
    Делаете общую функцию фильтрации, где получаете данные из всех фильтров и сравниваете их с элементами. Подключаете эту функцию ко всем фильтрам в качестве обработчика событий, возникающих при взаимодействии с ними пользователя. Например.
    Ответ написан
    1 комментарий
  • Как преобразовать данные?

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

    Первое:

    const widgetsTransform = Object
      .values(widgets.reduce((acc, n) => ((acc[n.type] = acc[n.type] ?? []).push(n), acc), {}))
      .map(n => n.length > 1 ? ({ type: `Section${n[0].type}`, children: n }) : n[0]);

    Второе:

    const widgetsTransform = widgets
      .reduce((acc, n, i, a) => (
        (i && n.type === a[~-i].type) || acc.push([]),
        acc[~-acc.length].push(n),
        acc
      ), [])
      .map(n => ~-n.length ? ({ type: 'Section' + n[0].type, children: n }) : n[0]);
    Ответ написан
    Комментировать
  • Из-за чего тут ошибка с фильтрацией по цене?

    0xD34F
    @0xD34F Куратор тега JavaScript
    Сравниваете строки вместо чисел. Скрываете элементы, но не показываете их.

    $('.price_min_max_btn').click(function() {
      const min = +$('.price_min').val() || 0;
      const max = +$('.price_max').val() || Infinity;
    
      $('.item_block_filter')
        .hide()
        .filter(function() {
          const price = +this.dataset.price;
          return min <= price && price <= max;
        })
        .show();
    });
    Ответ написан
    1 комментарий
  • Как получить позицию элемента относительно родителя?

    0xD34F
    @0xD34F Куратор тега JavaScript
    document.querySelector('table').addEventListener('click', e => {
      const td = e.target.closest('td');
      if (td) {
        // поскольку речь идёт о ячейках таблицы, то нет причин использовать что-то иное
        const index = td.cellIndex;
    
        // но вообще, можно и метод массива припахать
        const index = Array.prototype.indexOf.call(td.parentNode.children, td);
    
        // или вручную посчитать, как далеко удастся уйти по ссылкам на предыдущие элементы
        let index = 0;
        for (let el = td; el = el.previousElementSibling; index++) ;
      }
    });
    Ответ написан
    Комментировать
  • Как реализовать рекурсивную генерацию объекта по строке?

    0xD34F
    @0xD34F Куратор тега JavaScript
    А почему строка? Могли бы и сами догадаться её в массив порезать:

    const arr = str.split('-');
    const end = 'final';

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

    const obj = arr.reduceRight((acc, n) => ({ [n]: acc }), end);

    Но, конечно, если очень хочется, то рекурсия возможна:

    const obj = (function createObj(i) {
      return i < arr.length
        ? { [arr[i]]: createObj(i + 1) }
        : end;
    })(0);
    Ответ написан
    1 комментарий
  • Как объединить массивы?

    0xD34F
    @0xD34F Куратор тега JavaScript
    const result = graphData.map((n, i) => ({ ...n, color: palette[i % palette.length] }));

    или, в более общем виде:

    const combine = (arr, keys, values) =>
      arr.map(({ ...n }, i) => (
        values.forEach((m, j) => n[keys[j]] = m[i % m.length]),
        n
      ));
    
    const result = combine(graphData, [ 'color' ], [ palette ]);
    Ответ написан
    1 комментарий
  • Как найти и заменить несколько вхождений в тексте одним скриптом?

    0xD34F
    @0xD34F Куратор тега JavaScript
    const replacements = [
      [ 'вот это надо заменить', 'вот на это' ],
      [ 'а это заменить', 'на вот это' ],
    ];
    
    $elements.html((i, html) => {
      return replacements.reduce((acc, n) => acc.replace(RegExp(n[0], 'g'), n[1]), html);
    });
    Ответ написан
    Комментировать