Задать вопрос
  • Как можно заменить цвет #a8a3a0 на пример на квадратик белого цвета?

    0xD34F
    @0xD34F Куратор тега JavaScript
    const regex = /#[a-f\d]+;$/i;
    const replacement = '<span class="color" style="background: $&"></span>';
    
    document.querySelectorAll('селектор сами сообразите').forEach(n => {
      n.innerHTML = n.innerText.replace(regex, replacement);
    });
    Ответ написан
    2 комментария
  • Как разбить массив на подмассивы, отсортировать по рядам упорядоченных чисел?

    0xD34F
    @0xD34F Куратор тега JavaScript
    Делаем просто, ровно то, что спрошено в вопросе:

    const result = arr.reduce((acc, n, i, a) => (
      n === a[i - 1] + 1 || acc.push([]),
      acc.at(-1).push(n),
      acc
    ), []);

    Делаем сложно, решаем задачу в более общем виде (группируем элементы не только массивов, а любых итерируемых объектов; условие создания новой группы отделяем от собственно кода группировки):

    function groupAdjacent(
      data,
      {
        key = n => n,
        newGroup = (c, p) => c !== p,
      } = {}
    ) {
      const result = [];
      const getVal = key instanceof Function ? key : n => n[key];
      let prev = null;
      let i = -1;
    
      for (const n of data) {
        const curr = getVal(n, ++i);
    
        if (!result.length || newGroup(curr, prev)) {
          result.push([]);
        }
    
        result.at(-1).push(n);
        prev = curr;
      }
    
      return result;
    }

    Как применять в вашем случае:

    const result = groupAdjacent(arr, { newGroup: (c, p) => c !== -~p });
    // или
    const result = groupAdjacent(arr, { key: (n, i) => n - i });

    Какие ещё возможны способы применения:

    groupAdjacent(arr, { key: 'name' })
    groupAdjacent(arr, { newGroup: n => n === 1 })
    groupAdjacent(arr).map(n => n.length > 1 ? n : n[0])
    groupAdjacent(arr, { newGroup: n => /^\d+\./.test(n) })
    Ответ написан
    5 комментариев
  • Почему модальное окно всплывает только при нажатие на первую кнопку?

    0xD34F
    @0xD34F Куратор тега JavaScript
    Всплывает при нажатии на все кнопки. Но не то, которое надо, а первое. Которое видно только когда отображается первый же .product. Вместо первого .product нужен тот, внутри которого находится нажатая кнопка:

    document.querySelector('.wrapper_product') ---> event.target
    Ответ написан
    4 комментария
  • Вложенные массивы - как вытащить одно значение в цикле по самому низкому индексу?

    0xD34F
    @0xD34F
    $value = array_combine($data['value_index'], $data['value'])[min($data['value_index'])];

    или

    $value = $data['value'][array_search(min($data['value_index']), $data['value_index'])];

    или

    $index = 0;
    
    for ($i = 1; $i < count($data['value_index']); $i++) {
      if ($data['value_index'][$i] < $data['value_index'][$index]) {
        $index = $i;
      }
    }
    
    $value = $data['value'][$index];
    Ответ написан
    1 комментарий
  • Как скрывать текущий item(li) при открытие другого item(li)?

    0xD34F
    @0xD34F Куратор тега JavaScript
    Если прямо отвечать на спрошенное, то...
    menu.addEventListener('click', ({ target: t }) => {
      if (t.tagName === 'SPAN') {
        const parent = t.parentNode;
        parent.classList.toggle('active');
        for (const n of menu.querySelectorAll('.active')) {
          if (n !== parent) {
            n.classList.toggle('active', n.contains(parent));
          }
        }
      }
    });

    Но вообще, давайте-ка напишем чуть более универсальное решение.

    Что если количество уровней вложенности не будет ограничено числом два? Пусть скрытые элементы показываются только когда активен родитель, а не любой предок, т.е.:

    - .menu_list_item.active .submenu {
    + .active > .submenu {

    - .submenu_list_item.active .product {
    + .active > .product {

    Ну и ещё перестанем зашивать в обработчик клика селекторы и переключаемый класс, определим их отдельно, если вдруг что изменить понадобится, то достаточно будет отредактировать код всего в одном месте:

    const container = document.querySelector('#menu');
    const itemSelector = 'li';
    const buttonSelector = `${itemSelector} span`;
    const activeClass = 'active';
    
    container.addEventListener('click', function(e) {
      const item = e.target.closest(buttonSelector)?.closest(itemSelector);
      if (item) {
        item.classList.toggle(activeClass);
        this.querySelectorAll(`.${activeClass}`).forEach(n => {
          if (n !== item) {
            n.classList.toggle(activeClass, n.contains(item));
          }
        });
      }
    });
    Ответ написан
    3 комментария
  • Как избавиться от lodash?

    0xD34F
    @0xD34F Куратор тега JavaScript
    Открыть документацию lodash, разобраться, что делают используемые в представленном коде методы, переписать код без их использования.

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

    0xD34F
    @0xD34F Куратор тега JavaScript
    Если прямо отвечать на спрошенное, то...
    const items = document.querySelectorAll('.faq__input');
    const onChange = ({ target: t }) => t
      .closest('.faq__items')
      .querySelectorAll('.faq__question--top')
      .forEach(n => n.classList.toggle('active', n.nextElementSibling.checked));
    
    items.forEach(n => n.addEventListener('change', onChange));

    Но вообще, предлагаю назначать класс элементам, находящимся максимально высоко, самым дальним не общим предкам радиокнопок. Т.е., .faq__item вместо .faq__question--top. Если в будущем задумаете стилизовать внутри выбранного .faq__item элементы, находящиеся за пределами .faq__question--top, то не придётся переписывать js-код.

    Каким элементам надо переключать класс, что за радиокнопки находятся внутри, какой класс надо переключать, как класс переключать:

    const itemSelector = '.faq__item';
    const radioSelector = '.faq__input';
    const activeClass = 'active';
    const toggleActiveClass = radioGroupName => document
      .querySelectorAll(`${radioSelector}[name="${radioGroupName}"]`)
      .forEach(n => n.closest(itemSelector).classList.toggle(activeClass, n.checked));

    Можно назначить обработчик события каждой радиокнопке индивидуально:

    document.querySelectorAll(radioSelector).forEach(function(n) {
      n.addEventListener('change', this);
    }, e => toggleActiveClass(e.target.name));

    А можно назначить делегированный обработчик странице:

    document.addEventListener('change', ({ target: t }) => {
      if (t.matches(radioSelector)) {
        toggleActiveClass(t.name);
      }
    });
    Ответ написан
    2 комментария
  • Как правильно использовать функции array_filter() и in_array()?

    0xD34F
    @0xD34F
    $values = array_column($array2, 'value');
    $result = array_filter($array1, fn($n) => in_array($n->code, $values));
    Ответ написан
    3 комментария
  • Как обратиться к ключу объекта?

    0xD34F
    @0xD34F Куратор тега Vue.js
    из дочернего мне нужно передать именно ключ b в родительский

    Не нужно.

    В родительском компоненте вы знаете, кому какой ключ соответствует. Т.е., из дочернего достаточно просто просигналить о необходимости совершения какого-то действия, а родитель дальше сам разберётся. Типа так:

    Дочерний компонент: this.$emit('do-something').
    Родительский компонент:

    <child-component :object="objects.a" @do-something="doSomething('a')" />
    <child-component :object="objects.b" @do-something="doSomething('b')" />
    <!--> или <-->
    <child-component v-for="(v, k) in objects" :object="v" @do-something="doSomething(k)" />

    methods: {
      doSomething(key) {
        // чего-то делаете
      },
      ...

    Или даже проще, без ключей. Так как, по вашим же словам, ключ вы передаёте, чтобы

    затем пройтись по objects и при совпадении ключей (a, b или c) выполнить действия над значением

    , то почему бы сразу не обращаться к нужному значению?

    <child-component :object="objects.a" @do-something="doSomething(objects.a)" />
    <child-component :object="objects.b" @do-something="doSomething(objects.b)" />
    <!--> или <-->
    <child-component v-for="n in objects" :object="n" @do-something="doSomething(n)" />

    Ещё можно отправлять из дочернего компонента наверх сам объект: this.$emit('do-something', this.object). Тогда не придётся дублировать обращение к нему в родителе:

    <child-component :object="objects.a" @do-something="doSomething" />
    <child-component :object="objects.b" @do-something="doSomething" />
    <!--> или <-->
    <child-component v-for="n in objects" :object="n" @do-something="doSomething" />
    Ответ написан
    Комментировать
  • Как сделать рекурсию для списка комментариев?

    0xD34F
    @0xD34F Куратор тега Vue.js
    Функция, превращающая плоский массив в дерево:

    function createTree({
      arr,
      idKey = 'id',
      parentKey = 'parentId',
      childrenKey = 'children',
    }) {
      const tree = Object.fromEntries(arr.map(n => [ n[idKey], { ...n, [childrenKey]: [] } ]));
      return Object.values(tree).filter(n => !tree[n[parentKey]]?.[childrenKey].push(n));
    }

    Рекурсивный компонент v-tree, выводящий древовидные данные:

    props: {
      items: {
        type: Array,
        default: () => [],
      },
      maxdepth: {
        type: Number,
        default: Infinity,
      },
    },

    <ul v-if="Array.isArray(items) && items.length && maxdepth">
      <li v-for="n in items" :key="n.id">
        {{ n.value }}
        <v-tree :items="n.children" :maxdepth="maxdepth - 1" />
      </li>
    </ul>
    Ответ написан
    5 комментариев
  • Как собрать массив id на основе двух массивов?

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

    Может ли быть так, что какие-то из значений второго массива отсутствуют в первом? Если да - добавлять в результирующий массив какое-то дефолтное значение, или ничего не делать?

    Могут ли значения во втором массиве повторяться? Если да - сколько раз надо добавлять соответствующие им id в результирующий массив - один или согласно количеству повторений?

    Могут ли значений title в первом массиве быть неуникальны? Если да, какие из соответствующих им id помещать в результирующий массив - первые, последние или все, что есть?

    В общем, вариантов тут масса, выбирайте:

    computed: {
      ids() {
        return this.strings.map(n => this.objects.find(m => m.title === n)?.id);
      },
    
      // или
    
      ids() {
        const ids = this.objects.reduce((acc, n) => (acc[n.title] = n.id, acc), {});
        return this.strings.map(n => ids[n] ?? 'Получить id не удалось - объекта с указанным title нет');
      },
    
      // или
    
      idByTitle() {
        return Object.fromEntries(this.objects.map(n => [ n.title, n.id ]));
      },
      ids() {
        const ids = this.idByTitle;
        return this.strings.reduce((acc, n) => (
          Object.hasOwn(ids, n) && acc.push(ids[n]),
          acc
        ), []);
      },
    
      // или
    
      stringsSet() {
        return new Set(this.strings);
      },
      ids() {
        return this.objects.reduce((acc, n) => (
          this.stringsSet.has(n.title) && acc.push(n.id),
          acc
        ), []);
      },
    
      // или
    
      idsByTitle() {
        return this.objects.reduce((acc, n) => (
          (acc[n.title] ??= []).push(n.id),
          acc
        ), []);
      },
      ids() {
        return this.strings.flatMap(n => this.idsByTitle[n] ?? []);
      },
    },

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

    computed, к сожалению не подходит

    Подходит. Так во vue принято оформлять данные, зависящие от других данных. Что спросили - на то и ответил.

    Если вдруг у вас там vuex или pinia, то вместо computed надо поместить показанный код в getters, и в случае vuex также заменить this на обращение к параметрам - первый для стейта, второй для геттеров.

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

    0xD34F
    @0xD34F Куратор тега JavaScript
    const result = array2.filter(function(n) {
      return this.has(n.code);
    }, new Set(array1.map(n => n.value)));

    или

    const obj2 = Object.fromEntries(array2.map(n => [ n.code, n ]));
    const result = array1.reduce((acc, n) => ((n = obj2[n.value]) && acc.push(n), acc), []);

    или

    const result = array2.filter(n => array1.some(m => m.value === n.code));
    Ответ написан
    Комментировать
  • Метод addEventListener mouseover не работает над слайдами Swiper?

    0xD34F
    @0xD34F Куратор тега JavaScript
    Работает.

    e.target.dataset.followerStyle

    Что такое e.target? Ну, когда вы наводите курсор на слайд с data-атрибутом. На тот слайд, который не видно за вложенной в него картинкой.

    Надо не сразу target хватать, а пытаться найти у него предка с нужным вам атрибутом.
    Ответ написан
    1 комментарий
  • Как вычленить максимальные значения из массива объектов?

    0xD34F
    @0xD34F Куратор тега JavaScript
    Object.values(arr.reduce((max, n) => (
      max[n.group] = max[n.group]?.score > n.score ? max[n.group] : n,
      max
    ), {}))
    Ответ написан
    Комментировать
  • Почему у Proxy object лишние элементы в массиве?

    0xD34F
    @0xD34F Куратор тега JavaScript
    Как работает метод push, особое внимание на пятый и шестой пункты - устанавливается значение, устанавливается новая длина массива. Поскольку push вы взываете у proxy, а не у оригинального массива, то и при установке значения, и при установке новой длины срабатывает обработчик set.

    Как избавиться от такого поведения... Конечно, можно в обработчике set ничего не делать при попытке установить значение length. Правда, так вы тоже получите побочные эффекты, например - невозможность напрямую назначить длину массива; при выполнении pop'а, несмотря на то, что элемент будет удалён, длина массива не изменится и т.п.

    Так что стоит подумать - а действительно ли для решения стоящей перед вами задачи надо использовать proxy.
    Ответ написан
    Комментировать
  • Как создать массив объектов с определенными свойствами на основе другого массива объектов?

    0xD34F
    @0xD34F Куратор тега JavaScript
    // значения withSeat вместо раскладывания в отдельные переменные собираете в объект
    const withSeat = {
      adult: 1,
      teenager: 1,
      babe: 0,
    };
    
    
    const passengers = cabins.flatMap(n => Object
      .entries(n)
      .flatMap(([ k, v ]) => Array.from(
        { length: v },
        () => ({ withSeat: withSeat[k] })
      ))
    );
    Ответ написан
    6 комментариев
  • Как перебрать массив с элементами и при отличии первой буквы элемента с первой буквой следующего элемента добавлять эту букву перед элементом?

    0xD34F
    @0xD34F Куратор тега JavaScript
    arr.reduce((acc, n, i, a) => (
      a[i - 1]?.name[0] !== n.name[0] && acc.push({ name: n.name[0] }),
      acc.push(n),
      acc
    ), [])

    или

    arr.flatMap((n, i, a) =>
      i && a[~-i].name[0] === n.name[0]
        ? n
        : [ { name: n.name[0] }, n ]
    )
    Ответ написан
    Комментировать
  • Как создать объект из массива объектов c одинаковыми ключами?

    0xD34F
    @0xD34F Куратор тега JavaScript
    const obj = Object.fromEntries(Object
      .entries(arr
        .flatMap(Object.entries)
        .reduce((acc, n) => ((acc[n[0]] ??= new Set).add(n[1]), acc), {}))
      .map(([ k, v ]) => [ k, v.size === 1 ? [...v][0] : null ])
    );

    или

    const obj = arr.reduce((acc, n) => (
      Object.keys(n).forEach(k => {
        acc[k] = Object.hasOwn(acc, k) && n[k] !== acc[k] ? null : n[k];
      }),
      acc
    ), {});
    Ответ написан
    1 комментарий
  • Как работать с обновлением в инпуте при сортировке элементов?

    0xD34F
    @0xD34F Куратор тега Vue.js
    Не обновляйте данные, пока фокус не будет потерян. Типа, v-model.lazy.

    Или можете слушать событие input и дёргать метод focus у поля ввода, это заставит (ну, такое должно быть поведение по умолчанию) браузер выполнить прокрутку к элементу (если вдруг недостаточно, можно перед фокусом ещё и scrollIntoView вызвать). Типа так.
    Ответ написан
    1 комментарий