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

Браузерное окружение, 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"

Объект 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() и 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

Секция статьи "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'
          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"

Чтобы изменить адрес без перезагрузки страницы, используйте 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

Секция статьи "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')