• Как сделать, чтобы количество активных чекбоксов не опускалось ниже определённого?

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

    const container = document.querySelector('селектор общего предка чекбоксов');
    const checkboxSelector = 'селектор чекбоксов';
    const minChecked = 1;
    const maxChecked = Infinity;
    const countChecked = checkboxes =>
      Array.prototype.reduce.call(checkboxes, (acc, n) => acc + n.checked, 0);

    Если количество отмеченных чекбоксов меньше или равно минимально допустимому - блокируйте их, если количество отмеченных чекбоксов больше или равно максимально допустимому - блокируйте те, что не отмечены:

    const checkboxes = container.querySelectorAll(checkboxSelector);
    const onChange = () => {
      const count = countChecked(checkboxes);
      const minReached = count <= minChecked;
      const maxReached = count >= maxChecked;
      checkboxes.forEach(n => n.disabled = minReached && n.checked || maxReached && !n.checked);
    };
    
    checkboxes.forEach(n => n.addEventListener('change', onChange));

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

    container.addEventListener('change', function({ target: t }) {
      if (t.matches(checkboxSelector)) {
        const count = countChecked(this.querySelectorAll(checkboxSelector));
        t.checked ||= count < minChecked;
        t.checked &&= count <= maxChecked;
      }
    });
    Ответ написан
    3 комментария
  • Почему итератор нужно делать итерируемым?

    0xD34F
    @0xD34F Куратор тега JavaScript
    Посмотрите на результат выполнения следующего кода с [Symbol.iterator]() { return this; } и без:

    const iter = new Range(1, 5)[Symbol.iterator]();
    console.log(iter.next().value);
    console.log(iter.next().value);
    console.log([...iter]);
    Ответ написан
    2 комментария
  • Как получить индекс элемента с определенным классом на jQuery?

    0xD34F
    @0xD34F Куратор тега JavaScript
    Пытаюсь так:

    let currentIndex = $(".js-practice_button.current").index();

    Но значение всегда 0, у какой бы кнопки класс current не присутствовал.

    Потому что метод index по умолчанию определяет индекс элемента среди соседей, а так как у каждой кнопки есть отдельный родитель... Ну да, получаете то, что получаете.

    Можно вместо индекса кнопки определять индекс родителя:

    const index = $('.js-practice_button.current').closest('li').index();

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

    const index = $('.js-practice_button.current').index('.js-practice_button');

    А вообще, к чёрту jquery. Есть варианты и на чистом js:

    const index = Array.prototype.findIndex.call(
      document.querySelectorAll('.js-practice_button'),
      n => n.classList.contains('current')
    );
    
    // или
    
    const el = document.querySelector('.js-practice_button.current')?.parentNode;
    const index = el ? [...el.parentNode.children].indexOf(el) : -1;
    
    // или
    
    let index = -1;
    for (
      let el = document.querySelector('.js-practice_button.current')?.closest('li');
      el;
      el = el.previousElementSibling, index++
    ) ;
    Ответ написан
    Комментировать
  • Как удалить из массива числа с повторяющимися цифрами?

    0xD34F
    @0xD34F Куратор тега JavaScript
    Прежде чем браться за целый массив, разберёмся с одним числом. Есть разные способы узнать, все ли цифры в числе (правильно понимаю, что речь идёт о целых неотрицательных? - других-то вы не показали) являются уникальными, как относительно вменяемые, так и вполне дикие:

    const noRepeatingDigits = num => !/(\d).*\1/.test(num);
    
    // или
    
    const noRepeatingDigits = num => -~Math.log10(num) === new Set(`${num}`).size;
    
    // или
    
    const noRepeatingDigits = num => [...'' + num].every((n, i, a) => i === a.indexOf(n));
    
    // или
    
    const noRepeatingDigits = num => String(num)
      .split('')
      .reduce((acc, n) => (acc[n]++, acc), Array(10).fill(0))
      .every(n => n < 2);
    
    // или
    
    const noRepeatingDigits = num =>
      !''.match.call(num, /./g).some(function(n) {
        return this[n] = Object.hasOwn(this, n);
      }, {});
    
    // или
    
    const noRepeatingDigits = num =>
      !Array.from(num.toString()).sort().find((n, i, a) => n === a[i + 1]);

    Теперь массив. Можно удалить ненужное из существующего:

    arr.splice(0, arr.length, ...arr.filter(noRepeatingDigits));
    
    // или
    
    let numDeleted = 0;
    
    for (let i = 0; i < arr.length; i++) {
      const n = arr[i];
      arr[i - numDeleted] = n;
      numDeleted += !noRepeatingDigits(n);
    }
    
    arr.length -= numDeleted;
    
    // или
    
    for (let i = arr.length; i--; ) {
      if (!noRepeatingDigits(arr[i])) {
        arr.splice(i, 1);
      }
    }

    Или собрать новый:

    const newArr = arr.filter(noRepeatingDigits);
    
    // или
    
    const newArr = [];
    
    for (const n of arr) {
      if (noRepeatingDigits(n)) {
        newArr.push(n);
      }
    }
    
    // или
    
    const newArr = [];
    
    for (let i = 0; i < arr.length; i++) {
      if (noRepeatingDigits(arr[i])) {
        newArr[newArr.length] = arr[i];
      }
    }
    
    // или
    
    const newArr = (function xxx(arr, i = 0) {
      return i < arr.length
        ? (noRepeatingDigits(arr[i]) ? [ arr[i] ] : []).concat(xxx(arr, i + 1))
        : [];
    })(arr);
    Ответ написан
    1 комментарий
  • Как сделать равномерное слияние многомерного массива?

    0xD34F
    @0xD34F Куратор тега JavaScript
    const flatZip = arrs => arrs
      .reduce((acc, arr) => (
        arr.forEach((n, i) => (acc[i] ??= []).push(n)),
        acc
      ), [])
      .flat();
    
    
    const result = flatZip([
      [ 1, 2, 3, 4 ],
      [ 5, 6, 7, 8 ],
      [ 9, 10, 11 ],
    ]);
    Ответ написан
    Комментировать
  • Как отфильтровать элементы li по объекту?

    0xD34F
    @0xD34F Куратор тега JavaScript
    const li = Array.prototype.filter.call(
      document.querySelector('ul').children,
      function(n) {
        return this.every(([ k, v ]) => v === n.querySelector(`.${k}`).innerText);
      },
      Object.entries(items)
    );

    или

    const li = [];
    
    COLLECT_LI:
    for (const n of document.querySelectorAll('li')) {
      for (const k in items) {
        if (Object.hasOwn(items, k) && items[k] !== n.querySelector('.' + k).textContent) {
          continue COLLECT_LI;
        }
      }
    
      li.push(n);
    }
    Ответ написан
    4 комментария
  • Почему при обновлении состояния не рендерится компонент?

    0xD34F
    @0xD34F Куратор тега React
    Не выдумывайте, всё рендерится.

    Почему видимых изменений нет? Потому что обновляете одно состояние, а рендер выполняете на основе другого, items в App и items в ElementsList - это разные массивы. Не надо никакого useState в ElementList, выполняйте рендер на основе prop'а; метод onDelete перенесите в App и тоже передавайте в ElementList через props.
    Ответ написан
    1 комментарий
  • Миграция на VUE 3, не работает роутер?

    0xD34F
    @0xD34F Куратор тега Vue.js
    Для vue 3 предназначена четвёртая версия роутера, а не третья:

    - <script src="https://unpkg.com/vue-router@3/dist/vue-router.js"></script>
    + <script src="https://unpkg.com/vue-router@4/dist/vue-router.global.js"></script>

    А вот это надо вырезать:

    import VueRouter from 'vue-router'
    Ответ написан
  • Как получить данные маркеров, которые находятся в области видимости карты (vue-yandex-map)?

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

    <yandex-map
      ref="map"
      @map-was-initialized="onBoundsChange"
      @boundschange="onBoundsChange"
      ...
    >
      <ymap-marker
        v-for="n in markersData"
        ...
      >

    methods: {
      onBoundsChange() {
        const bounds = this.$refs.map.myMap.getBounds();
        this.markersData.forEach(n => {
          if (
            bounds[0][0] < n.coords[0] && n.coords[0] < bounds[1][0] &&
            bounds[0][1] < n.coords[1] && n.coords[1] < bounds[1][1]
          ) {
            // ...
          }
        });
      },
      ...
    Ответ написан
    Комментировать
  • Как объединись значения из двух массивов в один?

    0xD34F
    @0xD34F
    $extract = fn($keys, $item) => array_combine($keys, array_map(fn($k) => $item[$k], $keys));
    
    $grouped = [];
    $productKeys = [ 'product_id', 'sku', 'quantity' ];
    $orderKeys = array_diff(array_keys($arr[0] ?? []), $productKeys);
    
    foreach ($arr as $n) {
      $id = $n['order_id'];
      $grouped[$id] ??= $extract($orderKeys, $n);
      $grouped[$id]['products'][] = $extract($productKeys, $n);
    }
    Ответ написан
  • Почему не получается передать пользовательское событие родительскому компоненту?

    0xD34F
    @0xD34F Куратор тега Vue.js
    Всё получается, всё передаётся.

    метод testemit компонента question не отрабатывает

    А должен? Нет, не должен - вы его нигде не вызываете. Как и не используете в качестве обработчика событий - вижу, что $emit('testemit') есть, а вот @testemit="testemit" отсутствует.
    Ответ написан
  • Как искать по своим вопросам или ответам?

    0xD34F
    @0xD34F
    Поиск тут сильно так себе, так что искать лучше через гугл. Лично я использую запрос следующего вида:

    site:qna.habr.com <имя-пользователя> <тег> <чего ищем>

    Отобрать вопросы/ответы - подписываетесь на интересующие вас теги, заходите в профиль, раздел "подписки", подраздел "теги", появится список тегов - рядом с каждым будут ссылки на списки соответствующих вопросов и ответов (конечно, если таковые у вас есть). Но, разумеется, можно и напрямую пройти, если знать, как нужный url выглядит. Вот ваш laravel, например.
    Ответ написан
    3 комментария
  • Как удалять теги из массива, которые уже не используются?

    0xD34F
    @0xD34F Куратор тега JavaScript
    Зайдите с другой стороны - вместо удаления того, чего больше нет, получите то, что есть, и замените массив:

    tags = Array.from(new Set(data.flatMap(n => n.tags)));

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

    tags.splice(0, tags.length, ...new Set(data.flatMap(n => n.tags)));
    Ответ написан
    Комментировать
  • Как очищать все поля формы поиска, кроме одного?

    0xD34F
    @0xD34F Куратор тега Vue.js
    Сделать observable объект, содержащий ссылку на экземпляр компонента, в котором последний раз осуществлялся пользовательский ввод. Установить наблюдение за этой ссылкой, и если значение не совпадает с текущим экземпляром - сбрасывать значение input'а:

    const lastEdited = Vue.observable({ instance: null });

    props: [ 'value' ],
    methods: {
      onInput(e) {
        this.$emit('input', e.target.value);
        lastEdited.instance = this;
      },
    },
    created() {
      this.$watch(() => lastEdited.instance, val => {
        if (val && val !== this) {
          this.$emit('input', '');
        }
      });
    },
    beforeDestroy() {
      if (lastEdited.instance === this) {
        lastEdited.instance = null;
      }
    },

    <input :value="value" @input="onInput">

    https://jsfiddle.net/jLw096zm/

    Или можно шину событий применить. Если случилось изменение input'а пользователем, кидать событие, в качестве аргумента передавать ссылку на экземпляр компонента, в обработчике сравнивать значение аргумента и this, если не совпали - очищать input:

    const eventBus = new Vue();

    props: [ 'value' ],
    methods: {
      onInput(e) {
        this.$emit('input', e.target.value);
        eventBus.$emit('clear-other-inputs', this);
      },
    },
    created() {
      const clear = e => e !== this && this.$emit('input', '');
      eventBus.$on('clear-other-inputs', clear);
      this.$on('hook:beforeDestroy', () => eventBus.$off('clear-other-inputs', clear));
    },

    <input :value="value" @input="onInput">

    https://jsfiddle.net/jLw096zm/1/
    Ответ написан
    Комментировать
  • Почему не удается получить актуальное значение переменной из Pinia в компоненте?

    0xD34F
    @0xD34F Куратор тега Vue.js
    имеется pinia такого вида
    <...>
    export const useSearchStore = () => {

    Ну и где тут Pinia? Где вызов defineStore?

    const { search, updateSearchQuery } = useSearchStore();

    Так нельзя:

    Note that store is an object wrapped with reactive, meaning there is no need to write .value after getters but, like props in setup, we cannot destructure it

    Вы вообще как, документацию пробовали открывать?

    Исправляем:

    Стор:

    const useSearchStore = defineStore('search', () => {
      const search = ref('');
      const setSearch = val => search.value = val;
      return { search, setSearch };
    });

    Компонент search input:

    const searchStore = useSearchStore();
    const search = computed({
      get: () => searchStore.search,
      set: searchStore.setSearch,
    });

    <input v-model.trim="search">

    Компонент search:

    Не надо слушать никаких событий из search input, не надо трогать стор, уберите это всё отсюда.

    Корневой компонент:

    const searchStore = useSearchStore(); 
    watch(() => searchStore.search, val => console.log(val));
    Ответ написан
  • Задача leetcode, как вернуть связный список?

    0xD34F
    @0xD34F
    Уж точно не так, как это пытаетесь сделать вы, весь показанный вами код - мусор, лучшее, что можете с ним сделать - удалить. Никаких новых узлов пытаться создавать не надо, надо лишь обновить свойство next у существующих. Как именно - думайте сами или гуглите, задача разбиралась тысячи и тысячи раз.
    Ответ написан
    Комментировать
  • Как использовать computed внутри v-for?

    0xD34F
    @0xD34F Куратор тега Vue.js
    Никак не использовать. Хотите тут computed - вместо одного значения пусть предоставляет массив:

    const names = computed(() => list.map(n => (n.main ?? n.default).name));

    <div v-for="n in names">
      {{ n }}
    </div>
    Ответ написан
    Комментировать
  • Как пытаться поочерёдно загружать изображения, до первого успешно загруженного?

    0xD34F
    @0xD34F Куратор тега JavaScript
    async function getFirstLoadedImage(urls) {
      for (const url of urls) {
        try {
          return await loadImage(url);
        } catch (e) {}
      }
    
      throw 'ничего загрузить не удалось';
    }
    
    
    getFirstLoadedImage(resolutions.map(n => `https://i.ytimg.com/vi/${videoId}/${n}.jpg`))
      .then(img => document.body.append(img))
      .catch(console.error);

    или, без async/await:

    const getFirstLoadedImage = urls =>
      new Promise((resolve, reject) => {
        (function next(i) {
          if (i === urls.length) {
            reject('ничего загрузить не удалось');
          } else {
            loadImage(urls[i]).then(resolve, () => next(i + 1));
          }
        })(0);
      });
    Ответ написан
    Комментировать
  • Как в fancybox 4 (5) сгруппировать галерею с миниатюрами?

    0xD34F
    @0xD34F
    Существует версия метода bind, позволяющая ограничить действие селектора определённым контейнером. Так что получаем элементы .item-gallery, и вызываем bind для каждого из них отдельно:

    document.querySelectorAll('.item-gallery').forEach(n => {
      Fancybox.bind(n, '[data-fancybox="single"]', {});
    });
    Ответ написан
    Комментировать
  • Куда пропал вопрос?

    0xD34F
    @0xD34F
    Удалился вопрос на этом форуме

    Здесь не форум.

    у кого-то уже было такое?

    Да, у многих такое было. Это характерная особенность нарушителей правил.

    Модераторы, ответьте

    В ваших интересах не привлекать к себе внимания модераторов. Особенно подобным образом. Кстати, в правилах об этом прямо говорится - раздел 8.

    напишите пожалуйста причину

    Причину ищите у себя в почтовом ящике. Если, конечно, на момент удаления вопроса у вас было включено соответствующее уведомление.

    Или найдите ссылку на вопрос в истории браузера, откройте, вместо вопроса увидите причину удаления.
    Ответ написан
    Комментировать