Задать вопрос
  • .stopPropagation() на самом деле не останавливает распространение события?

    johnymkp
    @johnymkp Автор вопроса
    Ivan Ustûžanin, отличные понятные ответы, большое спасибо!
  • .stopPropagation() на самом деле не останавливает распространение события?

    johnymkp
    @johnymkp Автор вопроса
    Ivan Ustûžanin, а дефолтное действие должно выполняться только на цели, а не на каждом элементе через который проходит событие?

    Грубо говоря, допустим у тега при click было бы дефолтное действие вывести в консоль "Привет, мир!". И у нас было бы 5 div'ов, вложенных друг в друга. Мы щелкаем на самом внутреннем и событие начинает распространяться по этим div'ам. Тогда дефолтное действие должно выполниться только на "целевом" div? Т.е. мы увидим только одну надпись "Привет, мир!"?

    И если бы мы например повесили на самый внешний div обработчик с preventDefault, то мы бы таким образом сказали "не выполняй дефолтное действие на целевом div"? (Я-то поначалу думал, что вызов e.preventDefault() блокирует выполнение дефолтного поведения именно на текущем элементе, на котором обработчик сработал).
  • .stopPropagation() на самом деле не останавливает распространение события?

    johnymkp
    @johnymkp Автор вопроса
    Т.е. вы хотите сказать, что браузер после stopPropagation действительно прекращает обходить элементы дальше, и просто вызывает дефолт-действие сразу на цели?
  • Порождается ли событие в элементе или оно само по себе?

    johnymkp
    @johnymkp Автор вопроса
    Сергей Соколов, ваше объяснение мне нравится. Теперь однозначно понятно, что событие возникает только один раз. Браузер рассчитывает "конечную точку" этого события (кнопка, поле ввода и т.д.), создает объект события, а затем начинает обход дерева элементов до конечной точки и у каждого элемента проверяет, нет ли у него обработчика этого события. Если есть - он его вызывает.
    Написано
  • Порождается ли событие в элементе или оно само по себе?

    johnymkp
    @johnymkp Автор вопроса
    Aetae, это я понимаю, что объект, который попадает в обработчики - один и тот же.
    Вопрос-то в другом. Есть форма, а в ней разные поля ввода, чекбоксы и т.д. Мы вешаем обработчик на форму, вводим что-то в поле или переключаем чекбокс. Мы можем конечно сказать, что "Произошло событие change на чекбоксе". И в target у нас будет чекбокс, а в currentTarget - форма (если говорить конкретно про тот обработчик, который мы на форму повесили). Но ведь по сути-то сначала это событие произошло вовсе не на чекбоксе, оно произошло на window и просто самый глубокий элемент, до которого оно смогло распространиться - это чекбокс.
    Т.е. с технической точки зрения получается некорректно говорить, что "щелчок по чекбоксу спровоцировал событие".
    Написано
  • Порождается ли событие в элементе или оно само по себе?

    johnymkp
    @johnymkp Автор вопроса
    Lynn «Кофеман», не согласен про словоблудие. Скорее это попытка внести ясность в формулировки.
    Например, когда вы рассказываете про объект события, то как вы объясните, что такое target и currentTarget?
    Написано
  • Порождается ли событие в элементе или оно само по себе?

    johnymkp
    @johnymkp Автор вопроса
    Lynn «Кофеман», но можно ли говорить, что это событие "возникает" на каждом элементе по пути к кнопке? Вот, грубо говоря, сидишь на собеседовании и говоришь, "Щелкаем на кнопку. Событие сначала возникает на объекте Window, потом на объекте Document, и дальше оно возникает на всех объектах по пути к кнопке. Потом оно возникает на самой кнопке и идет обратно вверх, вновь возникая на каждом объекте до самого Window"?
    Я просто сам до терминологии дотошный, ментальную модель как это работает я уже составил, все понятно, но хочется еще и правильные слова подобрать.
    Написано
  • Как заставить отдельные блоки прокручиваться независимо?

    johnymkp
    @johnymkp Автор вопроса
    Сергей delphinpro, Решение интересное, попробовал - получилось. Но есть несколько вопросов:
    1) Использовать гриды для создания сетки - это нормально? Просто помню, что когда-то давно считалось, что использовать таблицы для верстки - это моветон. А гриды вроде как преемник таблицы. Удобен для отображения товаров в виде колонок-строк, а вот именно для верстки самой сетки страницы я пока не встречал гриды.
    2) Как поступать в случае адаптивности? Например, если в моем примере с флексами мне надо скрыть боковое меню, я просто ставлю `display: none` и оно исчезает, а блок контента безо всяких дополнений просто растягивается по всей ширине. В случае с гридом же блок контента становится урезанным по горизонтали.
    Написано
  • Почему при переносе элементов столбцы получаются разной ширины?

    johnymkp
    @johnymkp Автор вопроса
    Ankhena, а при чем здесь они? Это свойства не контейнера, а элементов. Элементы имеют такую ширину, какую имеют, я не хочу на них влиять через basis. Параметры расширения и сжатия я тоже не трогаю. Другое дело, что если ширина столбцов рассчитывается пропорционально самому широкому элементу, как сказал Ivan Ustûžanin выше, тогда понятно почему такая ширина у них неравномерная.
    Написано
  • Почему при переносе элементов столбцы получаются разной ширины?

    johnymkp
    @johnymkp Автор вопроса
    Увеличил шрифт одному из элементов в строке, она стала выше второй строки.
    Написано
  • Почему код внутри промиса работает, хотя this там undefined?

    johnymkp
    @johnymkp Автор вопроса
    Разобрался. Оказалось две ошибки:
    1) Концептуальная - сохранять объект indexeddb в событии onupgradeneeded это неправильно, потому что БД будет находиться в состоянии незавершенной транзакции обновления и не позволит сохранять в хранилище данные.
    2) Техническая - я не дождался окончания промиса из метода init, из-за чего метод save по сути вызывался до того как в this.database реально попал объект открытой indexeddb.

    Вот
    исправленный код
    const idbConfig = {
      version: 1,
      dbname: 'stats123',
      storages: [
        {
          name: "constants123",
          settings: { }
        }
      ]
    };
    
    class Database {
      // config
      // database  <-- Это поля класса
    
      constructor(config) {
        this.config = config;
      }
    
      init() {
        return new Promise((resolve, reject) => {
          const openRequest = indexedDB.open(this.config.dbname, this.config.version);
    
          openRequest.onupgradeneeded = (event) => {
            // Здесь нельзя перезаписывать this.database, потому что БД будет находиться в режиме незавершенной
            // upgrage-транзакции и все равно не позволит ничего в себя сохранить.
            const database = openRequest.result;
            this.createStorages(database);
          }
    
          openRequest.onsuccess = (event) => {
            // Перезаписывать this.database надо именно в этом событии, потому что зедсь транзакция обновления уже завершена
            // и БД готова к дальнейшей работе.
            // Это событие всегда вызывается после onupgradeneeded/
            this.database = openRequest.result;
            resolve(true);
          }
        })
      }
    
      createStorages(database) {
        this.config.storages.forEach(s => {
          if (!database.objectStoreNames.contains(s.name)) {
            database.createObjectStore(s.name, s.settings);
          }
        });
      }
    
      save(storage, data, key = null) {
        return new Promise((resolve, reject) => {
          const tx = this.database.transaction(storage, "readwrite");
          const st = tx.objectStore(storage);
          if (key) {
            st.add(data, key);
          } else {
            st.add(data);
          }
          resolve(data);
        })
      }
    }
    
    class ConstantsService {
      storage = "constants123";
      database;
      requestConfig;
    
      constructor(database) {
        this.database = database;
      }
    
      async loadConstants() {
        const gameVersion = await this.loadGameVersion();
        await this.saveOrUpdateGameVersion(gameVersion);
      }
    
      async loadGameVersion() {
        return {
          id: 132, 
          name: "pathched", 
          releaseDate: 1722470400
        };
      }
    
      async saveOrUpdateGameVersion(gameVersion) {
        await this.database.save(this.storage, gameVersion, "gameVersion");
      }
    }
    
    
    async function demo() {
      const database = new Database(idbConfig);
      // Тут надо было дождаться окончания промиса, иначе save вызывалось до того, как this.database успеет перезаписаться
      await database.init();
      const cs = new ConstantsService(database);
      cs.loadConstants();
    }
    
    demo();
    Написано
  • Почему код внутри промиса работает, хотя this там undefined?

    johnymkp
    @johnymkp Автор вопроса
    Вообще конечная проблема у меня с indexeddb связана. То, что описано у меня в посте, очень странно, но наверное это и правда чудит мишура, потому что в конечном итоге БД создается и хранилище тоже.

    Я не нашел песочницу, где мог бы опубликовать пример с indexeddb, но вот я попытался вырезать кусок из программы и минимизировать его, чтобы он при этом самостоятельно запускался и не отвлекал лишними деталями:
    Кусок кода
    const idbConfig = {
      version: 1,
      dbname: 'stats123',
      storages: [
        {
          name: "constants123",
          settings: { }
        }
      ]
    };
    
    class Database {
      // config  <-- Это поля класса
      // database  <-- Тут должен храниться объект открытой БД.
    
      constructor(config) {
        this.config = config;
        this.database = "ololo";  // Просто для наглядности, что поле не перезапишется.
      }
    
      init() {
        return new Promise((resolve, reject) => {
          const openRequest = indexedDB.open(this.config.dbname, this.config.version);
    
          openRequest.onupgradeneeded = (event) => {
            this.database = openRequest.result; // a) Здесь предполагалось сохранить объект открытой БД, чтобы не переоткрывать каждый раз.
            this.createStorages();
            resolve(true);
          }
        })
      }
    
      createStorages() {
        this.config.storages.forEach(s => {
          if (!this.database.objectStoreNames.contains(s.name)) {
            this.database.createObjectStore(s.name, s.settings);
          }
        });
      }
    
      save(storage, data, key = null) {
        return new Promise((resolve, reject) => {
          const tx = this.database.transaction(storage, "readwrite");  // b) Ошибка вываливается тут, из-за this.database = "ololo", т.е. как будто не перезаписалось.
          const st = tx.objectStore(storage);
          if (key) {
            st.add(data, key);
          } else {
            st.add(data);
          }
          resolve(data);
        })
      }
    }
    
    class ConstantsService {
      storage = "constants";
      database;  // <-- Это поле не под саму БД indexeddb, а под экземпляр класса Database.
      requestConfig;
    
      constructor(database) {
        this.database = database;
      }
    
      async loadConstants() {
        const gameVersion = await this.loadGameVersion();
        await this.saveOrUpdateGameVersion(gameVersion);
      }
    
      async loadGameVersion() {
        return {
          id: 132, 
          name: "pathched", 
          releaseDate: 1722470400
        };
      }
    
      async saveOrUpdateGameVersion(gameVersion) {
        await this.database.save(this.storage, gameVersion, "gameVersion");
      }
    }
    
    const database = new Database(idbConfig);
    database.init();
    const cs = new ConstantsService(database);
    cs.loadConstants();

    Идея такая была: делаем класс под работу с БД, создаем его экземпляр (пуcть Foo). Запускаем инициализацию, которая заключается в создании \ открытии БД. При этом объект открытой БД сохраняем в поле экземпляра, чтобы не приходилось каждый раз открывать БД заново.
    Всем другим объектам, которым в работе нужна БД, передаем Foo. В данном примере это работа с игровыми константами. Нужно сохранить константу - вызываем на Foo метод сохранения. Внутри Foo у нас уже открытая БД сохранена в поле database - так с ней и работаем, через это поле. Однако почему-то при заходе в метод save выясняется, что в this.database лежит "ololo", а не объект с открытой БД.
    Написано
  • Почему код внутри промиса работает, хотя this там undefined?

    johnymkp
    @johnymkp Автор вопроса
    Да вот в том-то и дело, что я тоже себе верю и знаю что не может там быть undefined, но браузер жестко газлайтит, что я уж и сомневаться начал. Вот ссылка со скрином браузера.
    Написано
  • Использует ли вебпак какие-нибудь плагины по умолчанию?

    johnymkp
    @johnymkp Автор вопроса
    Опытным путем удалось выяснить, что подключать плагин DefinePlugin вручную не обязательно. process.env.NODE_ENV ставится автоматически в такое же знаение как флаг --mode.

    Остальные части вопроса актуальны.
    Написано
  • Почему возникает ошибка при использовании переменной окружения в конфиге webpack?

    johnymkp
    @johnymkp Автор вопроса
    Спасибо, в самом деле с -- начало работать.
    Написано
  • Почему возникает ошибка при использовании переменной окружения в конфиге webpack?

    johnymkp
    @johnymkp Автор вопроса
    Aetae,
    webpack.config.js

    const path = require('path');
    
    module.exports = (settings) => {
      console.log("Hello, world!");
      сonsole.log(settings.mode);
    
      return {
        mode: settings.mode,
        entry: './src/index.js',
        output: {
          filename: 'main.js',
          path: path.resolve(__dirname, 'dist'),
          clean: true
        }
      }
    };


    package.json

    {
      "name": "mywebpack",
      "version": "1.0.0",
      "description": "",
      "main": "index.js",
      "scripts": {
        "build": "webpack"
      },
      "keywords": [],
      "author": "",
      "license": "ISC",
      "devDependencies": {
        "webpack": "^5.93.0",
        "webpack-cli": "^5.1.4"
      }
    }


    Команда запуска npm run build --env mode=production
    Ошибка:
    [webpack-cli] Unknown command or entry 'mode=production'
    [webpack-cli] 
    Run 'webpack --help' to see available commands and options


    P.S. Есть подозрение, что если в package.json в скрипте не указать хотя бы одну переменную, например `"build": "webpack --env ololo=nomatter"`, то при запуске скрипта, например `npm run build --env some=thing" объект env вообще не создается, даже если передавать переменные. А если в скрипте хотя бы одна переменная описана, тогда env создается, и в него накидывается все, что мы передаем при запуске.
    Написано
  • Чем технически отличаются dev-, prod- и peer-зависимости в Node JS?

    johnymkp
    @johnymkp Автор вопроса
    Lynn «Кофеман», а можете написать, что такое сплющивание дерева, о котором вы упоминали?
    ну с npm всё возможно. Он же попробует сплющить дерево и Foobar попадёт в область видимости приложения. Это прямо-таки стандартная ошибка новичков и не только.


    И еще вы в комментариях писали, что
    но поскольку в реальности Plug2 явно не вызывает Foobar, то в итоге он будет работать с Foobar@1 из верхнего уровня и привет два часа счастливого дебага.
    . Как в итоге такую проблему обычно решают?
    Написано
  • Чем технически отличаются dev-, prod- и peer-зависимости в Node JS?

    johnymkp
    @johnymkp Автор вопроса
    Дмитрий Беляев, вроде стало понятнее. Если перевести на какую-нибудь житейскую аналогию, то получается примерно так: есть Садовник и Охранник (ПакетА и ПакетВ). Садовник в своей работе использует Ножницы и Инсектицид (это его prod-зависимости, он держит их у себя в кармане (в своей личной папке node_modules)), а Охранник - Рацию и Пистолет (тоже prod-зависимости). А в целом Садовник и Охранник работают на Усадьбе (это их peer-зависимость). При этом каждый из них может пользоваться функциональностью усадьбы, например, ЗакрытьВорота или ВключитьВодуДляПоливаГазона. Соответственно, если Садовник вручную поливать не намерен, он указывает в своей пир-зависимости Усадьба 1.5.3 (в которой есть автополив), и если Садовника пытаются нанять на Усадьбу 1.2.0 (пытаются установить пакет в проект, где peer-зависимость неподходящей версии), он предупреждает, что не сможет работать на ней. То же и с Охранником - если Усадьба не огорожена по периметру забором, он патрулировать не намерен. Так что и у него есть свои требования к версии.

    Если же вернуться к пакетам, то если в библиотеке появился новый хук и плагин его использует, то этот плагин не сможет работать с более старой версией библиотеки, где этого хука еще не было.

    В целом получается, что если у какой-то библиотеки есть peer-зависимость, то это в 99% случаев означает, что библиотека не является чем-то самодостаточным, а предназначена для работы в паре со своей peer-зависимостью.
    Написано
  • Чем технически отличаются dev-, prod- и peer-зависимости в Node JS?

    johnymkp
    @johnymkp Автор вопроса
    Алексей Уколов,
    Я крепко запутался. Давайте определимся, что вы подразумеваете под плагином? Я например два вида могу представить:
    1) На примере джавы: нечто вроде драйвера-реализации JDBC. Есть интерфейс работы с БД, зафиксированный стандартом. Есть провайдеры, которые этот интерфейс реализуют. Вы берете реализацию, кидаете в определенную папочку, пишете нечто вроде Provider.createConnection(параметры), у вас происходит автоматический поиск подсунутых реализаций и как только она найдена, вам возвращается объект, через который вы работаете с БД. В этом случае явно прослеживается связь между "библиотекой" и "плагином". В роли библиотеки тут, грубо говоря, "джава", а в роли плагина - конкретная реализация интерфейса.
    2) Плагины вроде html-webpack-plugin, которые реально никак не связаны ни с какой библиотекой и делают какую-то свою мелочь, типа чистит папку при каждом билде или подключает скрипт в html-страницу.

    Второй вариант как будто бы не о нашем разговоре, потому что там зависимостями не пахнет, все как раз независимо. А как при первом варианте возможно, как вы говорите
    Библиотека плагином тоже не пользуется, ей от его существования ни горячо, ни холодно. Соответственно, у Foobar никаких зависимостей нет, у Plug зависимостей тоже нет, но в peer-зависимостях указано, что те, кто хочет Plug установить к себе в качестве зависимости, должны установить рядом с ним и Foobar тоже.
    , я представить не могу.

    Lynn «Кофеман», что значит сплющивание? Перенос зависимостей из собственной node_modules пакета в папку node_modules проекта?
    И что значит
    но поскольку в реальности Plug2 явно не вызывает Foobar, то в итоге он будет работать с Foobar@1 из верхнего уровня и привет два часа счастливого дебага.

    "явно не вызывает Foobar"? Хотя тут тоже надо уточнить, что именно вы под плагином имеете ввиду.
    Написано
  • Чем технически отличаются dev-, prod- и peer-зависимости в Node JS?

    johnymkp
    @johnymkp Автор вопроса
    Вопрос у меня в том, правильные ли я выводы сделал. Потому что если бы я просто спросил, "что такое peer-зависимость" например, меня бы просто отослали на один из десятков существующих ответов, а я их прочитал (и не только их) и все равно не понял, потому что в моем видении они не полные. Так что я расписал свое текущее понимание и через уточнения хочу-таки разобраться.
    Написано