Браузерное окружение, BOM

Время чтения: 7 мин

Кратко

Секция статьи "Кратко"

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

У разных окружений разные возможности и функциональность. В этой статье рассмотрим браузерное окружение и браузерную модель документа.

Окружение предоставляет языку дополнительные возможности и функции. Браузерное окружение, например, даёт возможность работать со страницами сайтов.

Представление этих дополнительных возможностей и функциональности в виде объектов, к которым у языка есть доступ — это объектная модель.

В случае с браузерным окружением — это объектная модель браузера (Browser Object Model, BOM). Она предоставляет доступ к navigator, location, fetch и другим объектам.

        
          
          /*                              BOM                              |    __________________________|______________________________________    |             |           |             |             |         |navigator      screen      location       fetch        history     ...*/
          /*
                              BOM
                              |
    __________________________|______________________________________
    |             |           |             |             |         |
navigator      screen      location       fetch        history     ...

*/

        
        
          
        
      

navigator

Секция статьи "navigator"

Объект navigator содержит информацию о браузере: название, версия, платформа, доступные плагины, доступ к буферу обмена и прочее. Это один из самых больших объектов в окружении.

Так, например, может выглядеть содержимое этого объекта в Safari:

содержимое navigator в Safari

Или, например, так в Chrome:

содержимое navigator в Chrome

С помощью этого объекта можно узнать, разрешён ли доступ к кукам, получить доступ к буферу обмена, геолокации, узнать, с какого браузера пользователь смотрит на страницу через userAgent.

Забавный факт: поле userAgent объекта navigator часто используется, чтобы определять, в каком именно браузере пользователь смотрит страницу сайта. Но читать его глазами достаточно трудно, поэтому лучше это дело оставить какому-нибудь парсеру.

Также с помощью navigator можно зарегистрировать Service Worker.

screen

Секция статьи "screen"

Объект screen содержит информацию об экране браузера.

содержимое объекта screen

width и height указывают ширину и высоту экрана в пикселях соответственно. avail-метрики указывают, сколько ширины и высоты доступно — то есть ширину и высоту с учётом интерфейсных деталей браузера типа полос прокрутки.

pixelDepth указывает количество битов на один пиксель экрана.

location

Секция статьи "location"

Объект location даёт возможность узнать, на какой странице мы находимся (какой у неё URL) и перейти на другую страницу программно.

Например, для страницы https://www.google.com?search?sxsrf=ALekK02Nk... этот объект будет содержать:

Содержимое объекта location

Внутри него есть полезные поля для того, чтобы искать нужную часть адреса.

  • href содержит URL целиком, включая в себя полный адрес хоста, страницы и все query-параметры.
  • host и hostname указывают имя хоста. Разница между ними в том, что host включает в себя порт. Если бы мы стучались не на [google.com](http://google.com), а на google.com:8080, то host содержал бы значение www.google.com:8080, в то время как hostname остался бы тем же.
  • pathname указывает путь от корня адреса до текущей страницы.
  • search указывает query-параметры, которые находятся в адресной строке, если они есть.
  • hash указывает хеш (ID элемента после #) страницы. Его ещё называют якорем, потому что при переходе на страницу с хешем браузер найдёт элемент с ID, равным этому хешу, и прокрутит страницу к этому элементу.

Если мы хотим изменить адрес и перейти на другую страницу, мы можем указать новый location.href:

        
          
          location.href = 'https://yandex.ru'
          location.href = 'https://yandex.ru'

        
        
          
        
      

И браузер перейдёт на страницу с адресом https://yandex.ru.

fetch

Секция статьи "fetch"

Fetch предоставляет возможность работы с сетью, с его помощью можно отправлять запросы на сервер.

        
          
          fetch('http://example.com/movies.json')  .then((response) => {    return response.json()  })  .then((data) => {    console.log(data)  })
          fetch('http://example.com/movies.json')
  .then((response) => {
    return response.json()
  })
  .then((data) => {
    console.log(data)
  })

        
        
          
        
      

history

Секция статьи "history"

history даёт доступ к истории браузера, которая ограничена текущей вкладкой. То есть с её помощью можно перейти на страницу назад, только если мы пришли с неё.

Чтобы перемещаться по истории назад и вперёд, можно использовать методы back и forward:

        
          
          // Перешли на страницу назадhistory.back()// Перешли на страницу вперёд.history.forward()
          // Перешли на страницу назад
history.back()

// Перешли на страницу вперёд.
history.forward()

        
        
          
        
      

Для более точного управления историей рекомендуется использовать pushState:

State — любые данные, которые связаны с переходом; бывает полезно, когда нужно разбить форму на несколько шагов, но при этом помнить, какие данные были введены на каждом из них — их можно сохранять в state:

        
          
          // state – объект, который можно потом получить в popstate. Если не нужен — можно передать nullconst state = { user_id: 5 }// title — заголовок для отображения во вкладке браузера:const title = 'Some Page Title'// url — адрес:const url = '/another-page/'history.pushState(state, title, url)
          // state – объект, который можно потом получить в popstate. Если не нужен — можно передать null
const state = { user_id: 5 }

// title — заголовок для отображения во вкладке браузера:
const title = 'Some Page Title'

// url — адрес:
const url = '/another-page/'

history.pushState(state, title, url)

        
        
          
        
      

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

localStorage, sessionStorage

Секция статьи "localStorage, sessionStorage"

Локальные хранилища используются, чтобы хранить какие-то данные в браузере пользователя.

Они удобны, когда мы не хотим отправлять данные на сервер, потому что они, например, промежуточные, или нужны только на клиенте.

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

        
          
          function saveToStorage(data) {  try {    window.localStorage.setItem('some-key', JSON.stringify(data))  } catch {    alert('Failed to save data to local storage.')  }}function loadFromStorage() {  try {    return JSON.parse(window.localStorage.getItem('some-key'))  } catch {    alert('Failed to load data from local storage.')  }}// Код обработки формыfunction nextStep() {  // При переходе сохраняем всё, что введено:  saveToStorage(formData)}
          function saveToStorage(data) {
  try {
    window.localStorage.setItem('some-key', JSON.stringify(data))
  } catch {
    alert('Failed to save data to local storage.')
  }
}

function loadFromStorage() {
  try {
    return JSON.parse(window.localStorage.getItem('some-key'))
  } catch {
    alert('Failed to load data from local storage.')
  }
}

// Код обработки формы

function nextStep() {
  // При переходе сохраняем всё, что введено:
  saveToStorage(formData)
}

        
        
          
        
      

Разница между localStorage и sessionStorage в длительности хранения данных. Данные из sessionStorage сотрутся, когда пользователь закроет вкладку с приложением. Данные же из localStorage не стираются сами по себе.

На практике

Секция статьи "На практике"

Саша Беспоясов

Секция статьи "Саша Беспоясов"

navigator

Секция статьи "navigator"

Чтобы проверить, есть ли в navigator необходимая вам фича, используйте in:

        
          
          if (`bluetooth` in navigator) {  // Есть доступ к Bluetooth API.}if (`serviceWorker` in navigator) {  // Есть доступ к Service Worker API.}//  ...
          if (`bluetooth` in navigator) {
  // Есть доступ к Bluetooth API.
}

if (`serviceWorker` in navigator) {
  // Есть доступ к Service Worker API.
}

//  ...

        
        
          
        
      

screen

Секция статьи "screen"

Чтобы узнать, сколько пикселей занимает по ширине экран без полосы прокрутки (актуально для Windows, где полоса прокрутки отнимает какое-то пространство), используйте availWidth:

        
          
          // Без учёта полосы:const screenWidth = screen.width// С учётом полосы прокрутки:const withoutScrollBar = screen.availWidth
          // Без учёта полосы:
const screenWidth = screen.width

// С учётом полосы прокрутки:
const withoutScrollBar = screen.availWidth

        
        
          
        
      

location

Секция статьи "location"

Чтобы программно перейти на другую страницу, используйте location.href:

        
          
          location.href = "/another-page"// Так браузер перейдёт на страницу// по адресу another-page на текущем сайте.location.href = "https://google.com"// так браузер перейдёт на другой сайт.
          location.href = "/another-page"
// Так браузер перейдёт на страницу
// по адресу another-page на текущем сайте.

location.href = "https://google.com"
// так браузер перейдёт на другой сайт.

        
        
          
        
      

Чтобы узнать полный путь от корня сайта, используйте location.pathname:

        
          
          // https://out-great-service.io/full/path/to/current/pageconst path = location.pathname// path === "/full/path/to/current/page"
          // https://out-great-service.io/full/path/to/current/page

const path = location.pathname
// path === "/full/path/to/current/page"

        
        
          
        
      

history

Секция статьи "history"

Чтобы изменить адрес без перезагрузки страницы, используйте history.pushState:

        
          
          const someData = {}history.pushState(someData, null, "/new/page/url")
          const someData = {}
history.pushState(someData, null, "/new/page/url")

        
        
          
        
      

Для передачи данных, ассоциированных с переходом, используйте первый аргумент в history.pushState, а для указания нового заголовка страницы — второй:

        
          
          const transitionData = { userName: "Alex" }const newPageTitle = "Hello world!"history.pushState(transitionData, newPageTitle, "/new/page/url")
          const transitionData = { userName: "Alex" }
const newPageTitle = "Hello world!"
history.pushState(transitionData, newPageTitle, "/new/page/url")

        
        
          
        
      

localStorage

Секция статьи "localStorage"

Используйте функции-обёртки, чтобы получать доступ к хранилищу безопасно и не забывать превращать данные в JSON-строку при записи и в объект при чтении:

        
          
          function saveToStorage(key, data) {  try {    // Если браузер не поддерживает localStorage,    // блок try обезопасит код от неожиданной ошибки.    window.localStorage.setItem(key', JSON.stringify(data));  }  catch {    alert('Failed to save data to local storage.');  }}function loadFromStorage(key) {  try {    return JSON.parse(window.localStorage.getItem(key));  }  catch {    alert('Failed to load data from local storage.');  }}saveToStorage('user', {name: 'Alex', age: 26});loadFromStorage('user');
          function saveToStorage(key, data) {
  try {
    // Если браузер не поддерживает localStorage,
    // блок try обезопасит код от неожиданной ошибки.
    window.localStorage.setItem(key', JSON.stringify(data));
  }
  catch {
    alert('Failed to save data to local storage.');
  }
}

function loadFromStorage(key) {
  try {
    return JSON.parse(window.localStorage.getItem(key));
  }
  catch {
    alert('Failed to load data from local storage.');
  }
}

saveToStorage('user', {name: 'Alex', age: 26});
loadFromStorage('user');