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

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

Один JavaScript в поле не воин — у JavaScript есть окружение, которое делает его таким мощным и популярным.

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

Кратко

Скопировано

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

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

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

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

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

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

*/

        
        
          
        
      

navigator

Скопировано

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

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

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

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

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

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

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

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

screen

Скопировано

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

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

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

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

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('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 даёт доступ к истории браузера, которая ограничена текущей вкладкой. То есть с её помощью можно перейти на страницу назад, только если мы пришли с неё.

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

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

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

        
        
          
        
      

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

Методы pushState() и replaceState() принимают три параметра:

  • state — любые данные, которые связаны с переходом;
  • unused — неиспользуемый параметр, который существует по историческим причинам;
  • url (необязательный) — url адрес новой записи в истории.
        
          
          // state — данные для новой записи в историиconst state = { user_id: 5 }// unused — для совместимости рекомендуется передавать пустую строкуconst unused = ''// url — url адрес который будет добавлен в адресную строкуconst url = '/another-page/'history.pushState(state, unused, url)
          // state — данные для новой записи в истории
const state = { user_id: 5 }

// unused — для совместимости рекомендуется передавать пустую строку
const unused = ''

// url — url адрес который будет добавлен в адресную строку
const url = '/another-page/'

history.pushState(state, unused, url)

        
        
          
        
      

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

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 необходимая вам фича, используйте 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

Скопировано

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

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

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

        
        
          
        
      

location

Скопировано

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

Перейти по адресу на текущем сайте:

        
          
          location.href = '/another-page'
          location.href = '/another-page'

        
        
          
        
      

Чтобы перейти на другой сайт, укажите его URL полностью:

        
          
          location.href = 'https://google.com'
          location.href = 'https://google.com'

        
        
          
        
      

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

        
          
          // https://out-great-service.io/full/path/to/current/pageconst path = location.pathnameconsole.log(path)// '/full/path/to/current/page'
          // https://out-great-service.io/full/path/to/current/page

const path = location.pathname
console.log(path)
// '/full/path/to/current/page'

        
        
          
        
      

history

Скопировано

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

        
          
          history.pushState(null, '', '/new/page/url')
          history.pushState(null, '', '/new/page/url')

        
        
          
        
      

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

        
          
          const transitionData = { userName: 'Alex' }history.pushState(transitionData, '', '/new/page/url')
          const transitionData = { userName: 'Alex' }
history.pushState(transitionData, '', '/new/page/url')

        
        
          
        
      

После изменения url может потребоваться изменить заголовок - используйте document.title.

        
          
          document.title = 'Это новая страница!'
          document.title = 'Это новая страница!'

        
        
          
        
      

localStorage

Скопировано

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

        
          
          function saveToStorage(key, data) {  try {    // Если браузер не поддерживает localStorage,    // блок try обезопасит код от неожиданной ошибки    window.localStorage.setItem(key, JSON.stringify(data))  }  catch {    console.error('Failed to save data to local storage.')  }}function loadFromStorage(key) {  try {    return JSON.parse(window.localStorage.getItem(key))  }  catch {    console.error('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 {
    console.error('Failed to save data to local storage.')
  }
}

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

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