Задать вопрос
  • Как заменить все цифры на странице?

    0xD34F
    @0xD34F Куратор тега JavaScript
    Какие есть косяки:
    • Перебираете вложенные узлы, но замену выполняете у того, что передан в функцию.
    • Заменять содержимое надо только у текстовых узлов - чтобы проверить тип, смотрим nodeType.
    • У текстовых узлов нет innerText. Есть textContent и nodeValue.
    • Зачем функции знать заранее, что за замена должна быть выполнена? Что будете делать, если понадобится выполнить другую замену - закопипастите функцию, заменив регулярку? Лучше добавим второй параметр - функцию, принимающую текущий текст и возвращающую новый.

    Исправляем:

    function replaceText(node, replacer) {
      if (node.nodeType === Node.ELEMENT_NODE) {
        node.childNodes.forEach(n => replaceText(n, replacer));
      } else if (node.nodeType === Node.TEXT_NODE) {
        node.textContent = replacer(node.textContent);
      }
    }
    
    
    replaceText(document.body, str => str.replace(/\d/g, 'hello, world!!'));

    Или, долой рекурсию и проверки типов узлов, можно сразу текстовые перебирать:

    function replaceText(node, replacer) {
      const iter = document.createNodeIterator(node, NodeFilter.SHOW_TEXT);
    
      for (let n = null; n = iter.nextNode();) {
        n.nodeValue = replacer(n.nodeValue);
      }
    }
    Ответ написан
    Комментировать
  • Как изменить порядок вызова обработчиков событий?

    0xD34F
    @0xD34F Куратор тега React
    Смотрим, что умеет addEventListener:

    useCapture

    A boolean value indicating whether events of this type will be dispatched to the registered listener before being dispatched to any EventTarget beneath it in the DOM tree.

    Соответственно, element.addEventListener(event, handler, true).
    Ответ написан
    Комментировать
  • Как при повторном добавлении товара в корзину вместо дублирования увеличивать его количество?

    0xD34F
    @0xD34F
    case ADD_PRODUCT:
      const item = state.products.find(n => n.id === action.payload.id);
      return {
        ...state,
        products: item
          ? state.products.map(n => n === item ? { ...n, count: n.count + 1 } : n)
          : [ ...state.products, { ...action.payload, count: 1 } ],
      };
    Ответ написан
    1 комментарий
  • Как сделать, что бы считало все три категории?

    0xD34F
    @0xD34F Куратор тега JavaScript
    document.querySelector('.ticket_content').addEventListener('input', e => {
      document.querySelector('.ticket_price').innerText =
        Array.prototype.reduce.call(
          e.currentTarget.querySelectorAll('.plus_minus'),
          (acc, n) => acc + n.querySelector('input').value * n.nextElementSibling.innerText,
          0
        );
    });
    Ответ написан
  • Как правильно рекурсивно обойти словарь?

    0xD34F
    @0xD34F
    def get_keys(arr):
      return sum((get_keys(sub) if len(sub := n['subCategories']) else [ n['key'] ] for n in arr), [])
    Ответ написан
    Комментировать
  • Как пронумеровать элементы?

    0xD34F
    @0xD34F Куратор тега JavaScript
    Вариант раз - индекс передаётся в коллбек forEach'а:

    document.querySelectorAll('.item').forEach((n, i) => {
      n.querySelectorAll('.card-item').forEach(m => m.textContent = `button ${i}`);
    });

    Два - индекс отдаёт итератор entries:

    for (const [ i, n ] of document.querySelectorAll('.item').entries()) {
      for (const m of n.getElementsByClassName('card-item')) {
        m.innerText = 'button ' + i;
      }
    }

    Три - самостоятельно обновляем значение переменной с индексом:

    const items = document.querySelector('.parent-list').children;
    for (let i = 0; i < items.length; i++) {
      const subitems = items[i].querySelector('.card-list').children;
      for (let j = 0; j < subitems.length; j++) {
        subitems[j].innerHTML = 'button '.concat(i);
      }
    }
    Ответ написан
    1 комментарий
  • Как выводить варианты ответов в случайном порядке?

    0xD34F
    @0xD34F Куратор тега Vue.js
    function shuffle(arr) {
      for (let i = arr.length; i > 1;) {
        const j = Math.random() * (i--) | 0;
        [ arr[i], arr[j] ] = [ arr[j], arr[i] ];
      }
    
      return arr;
    }

    computed: {
      shuffledAnswers() {
        return shuffle(Object.entries(this.questions[this.idx].answers));
      },
      ...

    <div v-for="[ key, answer ] in shuffledAnswers">
      ...

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

    "answers": {

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

    import usersData from "../questions.json";

    Не надо импортировать вопросы в компоненте теста, пусть они передаются в него через props.

    selectedAnswer: "",

    Всего один? Этого мало, надо запоминать все полученные ответы.

    count: 5,

    <div v-if="idx < count">

    Если завтра добавятся новые вопросы или удалятся какие-то из существующих, побежите изменять значение этого свойства? Оно не нужно, надо смотреть на длину массива с вопросами. Или, можно сделать вычисляемое свойство, представляющее текущий вопрос (просто извлекаем элемент из массива с вопросами по индексу текущего вопроса), и проверять, существует ли он.

    computed:{
      randomQuestions () {
        usersData.sort(() => Math.random() - 0.5)

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

    :disabled="selectedAnswer != ''"

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

    <button
      @click="nextQuestion"

    надо сделать такую же кнопку для перехода к предыдущему вопросу.

    document.querySelectorAll("input").forEach((el) => (el.checked = false));

    Не надо лезть в DOM руками. Чтобы при переходе к следующему вопросу сбрасывать выбор, достаточно пересоздавать элементы, назначив их общему предку ключ, зависящий от индекса (:key="индекс_вопроса"). Но это, конечно, костыльное решение. Правильно будет управлять радиокнопками основываясь на данных, через v-model.

    @change="answered($event)"

    answered(e) {
      this.selectedAnswer = e.target.value;
      if (this.selectedAnswer == this.questions[this.idx].correctAnswer) {
        this.correctAnswers++;

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

    https://jsfiddle.net/0L9ayx1u/1/
    Ответ написан
    1 комментарий
  • Почему не могу перейти к странице по прямому адресу?

    0xD34F
    @0xD34F Куратор тега Vue.js
    Потому что документацию не читаете:

    поскольку наше приложение — одностраничное, не сконфигурировав соответствующим образом сервер мы заставим пользователей получать ошибку 404, если они перейдут по http://oursite.com/user/id напрямую

    <...>

    всё, что нужно — единственная "резервная" запись в конфигурации сервера. Если URL не совпадает ни с одним статическим файлом, сервер должен просто отдать index.html, в котором и живёт наше приложение
    Ответ написан
    Комментировать
  • Как код переписать?

    0xD34F
    @0xD34F Куратор тега JavaScript
    const bullshitInsert = str => str
      .replace(/(?<=[2468])(?=[2468])/g, '*')
      .replace(/(?<=[13579])(?=[13579])/g, '-');

    или

    const bullshitInsert = str => Array
      .from(str, (n, i) => (n * str[i - 1] && !((n ^ str[i - 1]) & 1) ? '*-'[n & 1] : '') + n)
      .join('');
    Ответ написан
    3 комментария
  • Как объединить массив с повторяющимися объектами?

    0xD34F
    @0xD34F Куратор тега JavaScript
    Если тупо получать то, что указано в тексте вопроса, тогда

    const result = Object
      .entries(arr.reduce((acc, n) => ((acc[n.name] ??= []).push(n.value), acc), {}))
      .reduce((acc, [ k, v ]) => (acc[k] = v.length === 1 || v, acc), {});

    Но почему

    ThirdName: true

    ??

    В самом деле так и надо, или всё-таки там должна быть строка с датой из исходного массива? А что если значения повторяются? Вот вариант с получением только уникальных значений и без дурацкого true там, где значение одно:

    const result = Object
      .entries(arr.reduce((acc, n) => ((acc[n.name] ??= new Set).add(n.value), acc), {}))
      .reduce((acc, [ k, [...v] ]) => (acc[k] = ~-v.length ? v : v[0], acc), {});
    Ответ написан
    2 комментария
  • Как сократить запись в v-bind?

    0xD34F
    @0xD34F Куратор тега Vue.js
    Не надо indexOf, индексы доступны в v-for без каких-либо дополнительных телодвижений:

    <div v-for="(row, iRow) in rows" class="board__rows">
      <div
        v-for="(col, iCol) in columns"
        v-text="col + row"
        :class="[ [ 'white', 'black' ][(iRow ^ iCol) & 1], 'board__square' ]"
      ></div>
    </div>
    Ответ написан
    Комментировать
  • Почему state не обновляется корректно с использованием React.memo + useCallback?

    0xD34F
    @0xD34F Куратор тега React
    Всё обновляется корректно. Что написали, то и получили.

    Почему всегда рендерятся все экземпляры Child2 - потому что вместо обновления одного элемента data.list вы всегда обновляете все.

    Почему значения сбрасываются на дефолтные - потому что из-за useCallback data у вас всегда имеет начальное значение (ну, то, что было при первом рендеринге, то, которое initialData).

    Переделываем:

    Компонент Parent, обработчик onChange передаваемый в Child1, пусть вместо значения обновляемого свойства получает функцию, которая будет принимать предыдущее значение и возвращать новое:

    React.useCallback((key, val) => {
      setData(data => ({
        ...data,
        [key]: val(data[key]),
      }));
    }, [])

    Ну и переписываем его вызовы внутри Child1:

    onChange('list', list => list.map((n, i) => i === index
      ? { ...n, [prop]: value }
      : n
    ))

    onChange('description', () => e.target.value)
    Ответ написан
  • Как создать фильтр для значения с массивом?

    0xD34F
    @0xD34F Куратор тега JavaScript
    const filter = (arr, conditions) =>
      arr.filter(
        function(n) {
          return this.every(([ k, values ]) => values.some(v => v.includes(n[k])));
        },
        Object
          .entries(conditions)
          .map(n => [ n[0], [].concat(n[1]).map(String) ])
          .filter(n => n[1].length)
      );
    Ответ написан
    8 комментариев
  • Как грамотно использовать this в данном коде?

    0xD34F
    @0xD34F Куратор тега JavaScript
    А лучше всего просто объяснить что бы я в итоге допер сам!

    Объясняю: включаешь голову, думаешь, гуглишь, ещё думаешь, делаешь. Вот так всё просто.
    Ответ написан
    4 комментария
  • Как правильно работать с контекстом в react?

    0xD34F
    @0xD34F Куратор тега React
    Вначале удаляем элемент из массива, затем выбираем новый. Но получается так, что, иногда, выбор случайного элемента массива, происходит до удаления элемента из массива и он выбирает только что удаленный элемент.

    Не "удаляем" - создаётся новый массив, в котором может оказаться (а может и нет) на один элемент меньше. Не "иногда" - всегда выбор нового случайного элемента осуществляется из старого массива.

    Как правильно реализовать такую логику?

    Надо выбирать новый случайный элемент из нового массива, а не из старого. Переносим соответствующий вызов dispatch из обработчика клика в эффект:

    useEffect(() => {
      dispatch({
        type: SET_RND_NUM,
        payload: state.arr[Math.random() * state.arr.length | 0],
      });
    }, [ state.arr ]);
    Ответ написан
  • Как добавить сотрудника в список (таблицу)?

    0xD34F
    @0xD34F Куратор тега Vue.js
    Cannot read properties of undefined (reading 'lastname')

    Понятно, смотрим, где есть попытка чтения свойства lastname.

    async addNewEmployee({ commit }, employee) {
        await axios
            .post(`http://127.0.0.1:8000/api/v1/employees/`, {
                lastname: employee.lastname,

    Тоже понятно, смотрим, где вызывается addNewEmployee и что туда передаётся.

    @submit.prevent="addNewEmployee(employee)"

    А вот здесь непонятно - что такое employee? Такого свойства в компоненте нет. Надо добавить. Его содержимым будет то, что сейчас находится в хранилище - firstname, lastname, number - там, в хранилище, этим данным делать нечего.
    Ответ написан
    1 комментарий
  • Почему возникает TypeError: Illegal invocation при вызове drawImage у canvas'а?

    0xD34F
    @0xD34F Куратор тега JavaScript
    const { drawImage } = canvas.getContext('2d') as CanvasRenderingContext2D;
    
    drawImage(
      ...

    Откуда вы взяли, что методы контекста можно вызывать в отрыве от, м-м-м, КОНТЕКСТА?

    Что у вас происходит, поясняю на пальцах:

    const obj = {
      fucking_value: 666,
      fucking_method() {
        if (!this.hasOwnProperty('fucking_value')) {
          throw 'FUCK OFF';
        }
    
        console.log(this.fucking_value);
      },
    };
    
    const { fucking_method } = obj;
    fucking_method();

    Если вдруг не поняли, то...
    ...вот бы узнать - а какого хрена вы взялись за react (кстати, а при чём он здесь? да не при чём, как и ts), обладая нулевыми знаниями js?

    Ключевое слово this - бегом гуглить, разбираться, что это, зачем, к каким значениям в каких случаях даёт доступ.
    Ответ написан
    4 комментария
  • Как подставить данные из динамического элемента в динамический элемент с помощью JQuery?

    0xD34F
    @0xD34F Куратор тега JavaScript
    document.querySelector('.form').addEventListener('input', e => {
      const data = Array.from(
        e.currentTarget.children,
        n => Array.from(n.querySelectorAll('input'), m => m.value)
      );
    
      document.querySelector('.total').innerText = Array
        .from(document.querySelectorAll('.row'))
        .reduce((acc, n, i) => {
          data[i].forEach((m, j) => n.children[j + 1].innerText = m);
          return acc + (+data[i].at(-1) || 0);
        }, 0);
    });

    или

    const total = document.querySelector('.total');
    const form = document.querySelector('.form');
    const formRows = form.getElementsByClassName('d-flex');
    const tableRows = document.getElementsByClassName('row');
    
    form.addEventListener('input', ({ target: t }) => {
      const inputBox = t.closest('.input-box');
      const formRow = inputBox.parentNode;
      const iRow = Array.prototype.indexOf.call(formRows, formRow);
      const iCol = -~Array.prototype.indexOf.call(formRow.children, inputBox);
    
      tableRows[iRow].cells[iCol].textContent = t.value;
    
      if (iCol === formRow.children.length) {
        total.textContent = Array.prototype.reduce.call(
          formRows,
          (acc, n) => acc + (n.lastElementChild.children[0].value | 0),
          0
        );
      }
    });
    Ответ написан
    4 комментария
  • Как преобразовать древовидный массив в плоский?

    0xD34F
    @0xD34F Куратор тега JavaScript
    Рекурсия есть:

    const flat = (arr, childrenKey) =>
      arr instanceof Array
        ? arr.flatMap(({ [childrenKey]: c, ...n }) => [ n, ...flat(c, childrenKey) ])
        : [];
    
    
    const result = flat(items, 'children');

    Рекурсии нет:

    function flat(arr, childrenKey) {
      const result = [];
    
      for (const stack = [...arr].reverse(); stack.length;) {
        const { [childrenKey]: c, ...n } = stack.pop();
    
        if (Array.isArray(c) && c.length) {
          stack.push(...[...c].reverse());
        }
    
        result.push(n);
      }
    
      return result;
    }
    
    // или
    
    function flat(arr, childrenKey) {
      const result = [];
      const stack = [];
    
      for (let i = 0; i < arr.length || stack.length; i++) {
        if (i === arr.length) {
          [ i, arr ] = stack.pop();
        } else {
          const { [childrenKey]: c, ...n } = arr[i];
    
          if (c?.constructor === Array && c.length) {
            stack.push([ i, arr ]);
            [ i, arr ] = [ -1, c ];
          }
    
          result.push(n);
        }
      }
    
      return result;
    }
    
    // или
    
    function flat([...arr], childrenKey) {
      for (const [ i, { [childrenKey]: c, ...n } ] of arr.entries()) {
        arr.splice(i, 1, n, ...(c?.[Symbol.iterator] && typeof c !== 'string' ? c : []));
      }
    
      return arr;
    }
    Ответ написан
    1 комментарий