Spec-Zone.ru › HTTP

Обнаружение браузера с помощью пользовательского агента

Обнаружение браузера с помощью пользовательского агента

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

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

Примечание: Стоит повторить: очень редко использование анализа пользовательского агента — хорошая идея. Почти всегда можно найти лучший и более совместимый способ решения вашей проблемы!

Соображения перед использованием обнаружения браузера

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

Вы пытаетесь обойти конкретную ошибку в какой-либо версии браузера?

Посмотрите или спросите в специализированных форумах: вы, скорее всего, не первый столкнулись с этой проблемой. Кроме того, эксперты или люди с другой точки зрения могут предложить идеи для обхода ошибки. Если проблема кажется необычной, стоит проверить, была ли эта ошибка сообщена поставщику браузера через их систему отслеживания ошибок (Mozilla; WebKit; Blink; Opera). Производители браузеров обращают внимание на сообщения об ошибках, и анализ может намекнуть на другие способы обхода ошибки.

Вы пытаетесь проверить существование определённой функции?

Ваш сайт должен использовать определённую функцию веб-стандартов, которую некоторые браузеры ещё не поддерживают, и вы хотите перенаправить этих пользователей на более старый веб-сайт с меньшим количеством функций, но который, как вы знаете, будет работать. Это худшая причина для использования обнаружения пользовательского агента, потому что есть высокая вероятность, что в конечном итоге все остальные браузеры догонят. Кроме того, непрактично тестировать каждый менее популярный браузер и проверять наличие этих веб-функций. Вы никогда не должны использовать анализ пользовательского агента. Всегда существует альтернатива — использование обнаружения функций.

Вы хотите предоставить разный HTML в зависимости от используемого браузера?

Обычно это плохая практика, но в некоторых случаях это необходимо. В этих случаях вы должны сначала проанализировать свою ситуацию, чтобы убедиться, что это действительно необходимо. Можете ли вы предотвратить это, добавив некоторые несемантические <div> или <span> элементы? Сложность успешного использования обнаружения пользовательского агента стоит небольших нарушений чистоты вашего HTML. Также пересмотрите свой дизайн: можете ли вы использовать поэтапное улучшение или адаптивный макет, чтобы избежать необходимости делать это?

Избегание определения пользователя агента

Если вы хотите избежать использования определения пользователя агента, у вас есть варианты!

Обнаружение функций

Обнаружение функций — это подход, при котором вы не пытаетесь выяснить, какой браузер отображает вашу страницу, а вместо этого проверяете, доступна ли необходимая вам функция. Если нет, вы используете резервный вариант. В тех редких случаях, когда поведение отличается между браузерами, вместо проверки строки пользователя агента вы должны реализовать тест для определения того, как браузер реализует API, и определить, как его использовать на основе этого. Пример обнаружения функций следующий. В 2017 году Chrome снял флаг экспериментальной поддержки lookbehind в регулярных выражениях, но ни один другой браузер не поддерживал его. Поэтому вы могли подумать, что следует сделать так:

// This code snippet splits a string in a special notation
let splitUpString;
if (navigator.userAgent.includes("Chrome")) {
  // YES! The user is suspected to support look-behind regexps
  // DO NOT USE /(?<=[A-Z])/. It will cause a syntax error in
  // browsers that do not support look-behind expressions
  // because all browsers parse the entire script, including
  // sections of the code that are never executed.
  const camelCaseExpression = new RegExp("(?<=[A-Z])");
  splitUpString = (str) => String(str).split(camelCaseExpression);
} else {
  // This fallback code is much less performant, but works
  splitUpString = (str) => str.replace(/[A-Z]/g, "z$1").split(/z(?=[A-Z])/g);
}

console.log(splitUpString("fooBare")); // ["fooB", "are"]
console.log(splitUpString("jQWhy")); // ["jQ", "W", "hy"]

Приведенный выше код содержал бы несколько неверных предположений: во-первых, предполагалось, что все строки пользовательских агентов, содержащие подстроку "Chrome", относятся к Chrome. Строки UA часто вводят в заблуждение. Затем предполагалось, что функция lookbehind всегда будет доступна, если браузер — Chrome. Агент может быть более старой версией Chrome, до добавления поддержки, или (поскольку функция была экспериментальной в то время) это может быть более поздняя версия Chrome, в которой она была удалена. Важнее всего, предполагалось, что никакие другие браузеры не будут поддерживать эту функцию. Поддержка могла быть добавлена в другие браузеры в любое время, но этот код по-прежнему выбирал бы худший путь.

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

let isLookBehindSupported = false;

try {
  new RegExp("(?<=)");
  isLookBehindSupported = true;
} catch (err) {
  // If the agent doesn't support look behinds, the attempted
  // creation of a RegExp object using that syntax throws and
  // isLookBehindSupported remains false.
}

const splitUpString = isLookBehindSupported
  ? (str) => String(str).split(new RegExp("(?<=[A-Z])"))
  : (str) => str.replace(/[A-Z]/g, "z$1").split(/z(?=[A-Z])/g);

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

Наконец, приведенные выше фрагменты кода поднимают критический вопрос о кроссбраузерном кодировании, который всегда следует учитывать. Не используйте непреднамеренно API, для которого вы тестируете, в неподдерживаемых браузерах. Это может показаться очевидным и простым, но иногда это не так. Например, в приведенных выше фрагментах кода использование lookbehind в краткой записи regexp (например, /reg/igm) приведет к ошибке парсера в неподдерживаемых браузерах. Таким образом, в приведенном выше примере вы будете использовать new RegExp("(?<=look_behind_stuff)"); вместо /(?<=look_behind_stuff)/, даже в части кода, поддерживающей lookbehind.

Постепенное улучшение

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

Плавное снижение

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

Обнаружение мобильных устройств

Вероятно, наиболее распространенное использование и злоупотребление определением пользователя агента заключается в определении устройства как мобильного. Однако люди слишком часто упускают из виду то, что они на самом деле ищут. Люди используют определение пользователя агента для определения того, является ли устройство пользователя подходящим для сенсорного управления и имеет ли маленький экран, чтобы оптимизировать свой сайт соответственно. Хотя определение пользователя агента иногда может обнаружить это, не все устройства одинаковы: некоторые мобильные устройства имеют большой размер экрана, некоторые настольные компьютеры имеют небольшой сенсорный экран, некоторые люди используют смарт-телевизоры, которые представляют собой совершенно другую категорию, и некоторые люди могут динамически изменять ширину и высоту своего экрана, поворачивая планшет! Таким образом, определение пользователя агента определенно не лучший способ. К счастью, существуют гораздо лучшие альтернативы. Используйте Navigator.maxTouchPoints, чтобы определить, имеет ли устройство пользователя сенсорный экран. Затем вернитесь к проверке экрана пользователя агента только в случае if (!("maxTouchPoints" in navigator)) { /*Код здесь*/}. Используя эту информацию о наличии сенсорного экрана на устройстве, не меняйте всю структуру веб-сайта только для сенсорных устройств: это только создаст больше работы и обслуживания для вас. Вместо этого добавьте удобства для сенсорных устройств, такие как более крупные и легко нажимаемые кнопки (это можно сделать с помощью CSS, увеличив размер шрифта). Вот пример кода, увеличивающего отступ #exampleButton до 1em на мобильных устройствах.

let hasTouchScreen = false;
if ("maxTouchPoints" in navigator) {
  hasTouchScreen = navigator.maxTouchPoints > 0;
} else if ("msMaxTouchPoints" in navigator) {
  hasTouchScreen = navigator.msMaxTouchPoints > 0;
} else {
  const mQ = matchMedia?.("(pointer:coarse)");
  if (mQ?.media === "(pointer:coarse)") {
    hasTouchScreen = !!mQ.matches;
  } else if ("orientation" in window) {
    hasTouchScreen = true; // deprecated, but good fallback
  } else {
    // Only as a last resort, fall back to user agent sniffing
    const UA = navigator.userAgent;
    hasTouchScreen =
      /\b(BlackBerry|webOS|iPhone|IEMobile)\b/i.test(UA) ||
      /\b(Android|Windows Phone|iPad|iPod)\b/i.test(UA);
  }
}

if (hasTouchScreen) {
  document.getElementById("exampleButton").style.padding = "1em";
}

Что касается размера экрана, используйте window.innerWidth и window.addEventListener("resize", () => { /*обновить размер экрана*/ }). Цель для размера экрана — не удалять информацию на меньших экранах. Это только раздразит людей, потому что заставит их использовать настольную версию. Вместо этого попробуйте иметь меньше столбцов информации на длинной странице на меньших экранах и больше столбцов с короткой страницей на больших экранах. Этот эффект можно легко получить с помощью CSS гибких контейнеров, иногда с плавающими элементами как частичным резервным вариантом.

Также попробуйте перенести менее релевантную/важную информацию в нижнюю часть и объединить содержимое страницы осмысленным образом. Хотя это не по теме, возможно, следующий подробный пример даст вам идеи и вдохновение, чтобы отказаться от определения пользователя агента. Представим себе страницу, состоящую из блоков информации; каждый блок посвящен определенной породе кошек или собак. Каждый блок содержит изображение, обзор и забавный исторический факт. Картинки сохраняются с максимальным разумным размером даже на больших экранах. В целях осмысленного группирования содержимого все блоки с кошками отделены от всех блоков с собаками так, что блоки с кошками и собаками не смешиваются. На большом экране экономится место, если вы используете несколько столбцов, чтобы уменьшить пустое пространство слева и справа от изображений. Блоки могут быть разделены на несколько столбцов с помощью двух равно справедливых методов. С этого момента мы будем предполагать, что все блоки с собаками находятся в верхней части исходного кода, все блоки с кошками — в нижней части исходного кода, и все эти блоки имеют один и тот же родительский элемент. Есть, конечно, один случай, когда блок с собакой непосредственно предшествует блоку с кошкой. Первый метод использует горизонтальные гибкие контейнеры, чтобы сгруппировать содержимое так, чтобы при отображении страницы пользователю все блоки с собаками находились вверху страницы, а все блоки с кошками — ниже. Второй метод использует структуру столбцов и размещает всех собак слева, а всех кошек — справа. Только в этом конкретном случае уместно не предоставлять резервного варианта для гибких контейнеров/многостолбцовых элементов, что приведет к одному столбцу очень широких блоков в старых браузерах. Также обратите внимание на следующее. Если больше людей посещает страницу, чтобы посмотреть кошек, то может быть разумно поместить всех кошек выше в исходном коде, чем собак, так что больше людей смогут быстрее найти то, что ищут, на меньших экранах, где содержимое сворачивается в один столбец.

Далее, делайте свой код динамичным. Пользователь может повернуть своё мобильное устройство, изменив ширину и высоту страницы. Или в будущем может появиться какое-то странное устройство типа «раскладушки», где его открытие увеличит экран. Не будьте тем разработчиком, у которого болит голова от того, как справиться с подобным устройством «раскладушки». Никогда не будьте довольны своим веб-сайтом, пока вы не сможете открыть боковую панель инструментов разработчика и изменить размер экрана, при этом веб-сайт выглядит гладко, плавно и динамически масштабируется. Самый простой способ сделать это — выделить весь код, который перегруппировывает содержимое на основе размера экрана в одну функцию, которая вызывается при загрузке страницы и при каждом последующем событии изменения размера. Если эта функция макета рассчитывает много данных, прежде чем определить новый макет страницы, рассмотрите отложенное выполнение обработчика событий, чтобы он не вызывался так часто. Также обратите внимание на огромную разницу между запросами к медиа (max-width: 25em), not all and (min-width: 25em), и (max-width: 24.99em): (max-width: 25em) исключает (max-width: 25em), тогда как not all and (min-width: 25em) включает (max-width: 25em). (max-width: 24.99em) — это упрощенная версия not all and (min-width: 25em): не используйте (max-width: 24.99em), потому что макет, возможно, сломается на очень больших размерах шрифтов на устройствах с очень высоким разрешением в будущем. Всегда очень тщательно выбирайте правильный запрос к медиа и правильные знаки >=, <=, > или < в соответствующем JavaScript, потому что очень легко их перепутать, что приведет к тому, что веб-сайт будет выглядеть странно именно при размере экрана, где происходит изменение макета. Поэтому тщательно тестируйте веб-сайт в точных значениях ширины/высоты, где происходят изменения макета, чтобы убедиться, что изменения макета происходят должным образом.

Максимальное использование определения пользователя агента

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

Один из таких случаев — использование определения пользователя агента в качестве резервного варианта при определении наличия сенсорного экрана на устройстве. Дополнительную информацию см. в разделе Обнаружение мобильных устройств.

Другой такой случай — исправление ошибок в браузерах, которые не обновляются автоматически. Internet Explorer (в Windows) и Webkit (в iOS) — два отличных примера. До версии 9 Internet Explorer имел проблемы с ошибками отображения, ошибками CSS, ошибками API и т. д. Однако до версии 9 Internet Explorer было очень легко определить на основе имеющихся функций, специфичных для браузера. Webkit немного хуже, потому что Apple вынуждает все браузеры на iOS использовать Webkit внутри, поэтому у пользователя нет возможности получить лучший и более обновленный браузер на старых устройствах. Большинство ошибок можно обнаружить, но некоторые ошибки требуют больше усилий для обнаружения, чем другие. В таких случаях использование определения пользователя агента может быть полезным для экономии производительности. Например, в Webkit 6 есть ошибка, из-за которой при изменении ориентации устройства браузер может не вызывать обработчики MediaQueryList, когда должен.

const UA = navigator.userAgent;
const isWebkit =
  /\b(iPad|iPhone|iPod)\b/.test(UA) &&
  /WebKit/.test(UA) &&
  !/Edge/.test(UA) &&
  !window.MSStream;

let mediaQueryUpdated = true;
const mqL = [];

function whenMediaChanges() {
  mediaQueryUpdated = true;
}

const listenToMediaQuery = isWebkit
  ? (mQ, f) => {
      if (/height|width/.test(mQ.media)) {
        mqL.push([mQ, f]);
      }
      mQ.addListener(f);
      mQ.addListener(whenMediaChanges);
    }
  : () => {};

const destroyMediaQuery = isWebkit
  ? (mQ) => {
      for (let i = 0; i < mqL.length; i++) {
        if (mqL[i][0] === mQ) {
          mqL.splice(i, 1);
        }
      }
      mQ.removeListener(whenMediaChanges);
    }
  : listenToMediaQuery;

let orientationChanged = false;
addEventListener(
  "orientationchange",
  () => {
    orientationChanged = true;
  },
  PASSIVE_LISTENER_OPTION
);

addEventListener("resize", () =>
  setTimeout(() => {
    if (orientationChanged && !mediaQueryUpdated) {
      for (let i = 0; i < mqL.length; i++) {
        mqL[i][1](mqL[i][0]);
      }
    }
    mediaQueryUpdated = orientationChanged = false;
  }, 0)
);

Какая часть строки пользователя агента содержит искомую информацию?

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

Имя браузера

Когда люди говорят, что хотят «обнаружения браузера», часто они на самом деле хотят «обнаружения рендерингового движка». Хотите ли вы на самом деле обнаружить Firefox, а не SeaMonkey, или Chrome, а не Chromium? Или вы хотите увидеть, использует ли браузер рендеринговый движок Gecko или WebKit? Если это то, что вам нужно, см. ниже на странице.

Большинство браузеров устанавливают имя и версию в формате ИмяБраузера/НомерВерсии, за исключением Internet Explorer. Но так как имя — не единственная информация в строке пользователя, которая имеет этот формат, вы не можете определить имя браузера, вы можете только проверить, ищете ли вы нужное имя. Но обратите внимание, что некоторые браузеры лгут: Chrome, например, сообщает как Chrome, так и Safari. Поэтому, чтобы обнаружить Safari, нужно проверить строку Safari и отсутствие строки Chrome, Chromium часто сообщает о себе как о Chrome, а Seamonkey иногда сообщает о себе как о Firefox.

Также обратите внимание, что не следует использовать простое регулярное выражение для имени браузера, поскольку строки пользователя также содержат строки за пределами синтаксиса «КлючевоеСлово/Значение». Safari и Chrome содержат строку «like Gecko», например.

Движок Должен содержать Не должен содержать
Firefox Firefox/xyz Seamonkey/xyz
Seamonkey Seamonkey/xyz
Chrome Chrome/xyz Chromium/xyz
Chromium Chromium/xyz
Safari Safari/xyz Chrome/xyz или Chromium/xyz
Opera 15+ (движок Blink) OPR/xyz
Opera 12- (движок Presto) Opera/xyz
Internet Explorer 10- ; MSIE xyz;
Internet Explorer 11 Trident/7.0; .*rv:xyz

[1] Safari предоставляет два номера версий: один технический в марке Safari/xyz, а другой удобный для пользователя в марке Version/xyz.

Конечно, нет никакой гарантии, что другой браузер не перехватит некоторые из этих вещей (как Chrome в прошлом перехватывал строку Safari). Вот почему обнаружение браузера с использованием строки пользователя ненадежно и должно выполняться только с проверкой номера версии (перехват предыдущих версий менее вероятен).

Версия браузера

Версия браузера часто, но не всегда, помещается в часть значения маркера ИмяБраузера/НомерВерсии в строке пользователя. Это, конечно, не относится к Internet Explorer (который помещает номер версии сразу после маркера MSIE) и к Opera после версии 10, которая добавила маркер Версия/НомерВерсии.

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

Рендеринговый движок

Как уже говорилось ранее, в большинстве случаев поиск рендерингового движка — лучший способ. Это поможет не исключать менее известные браузеры. Браузеры, использующие один и тот же рендеринговый движок, будут отображать страницу одинаково: часто можно справедливо предположить, что то, что будет работать в одном, будет работать и в другом.

Существует пять основных рендеринговых движков: Trident, Gecko, Presto, Blink и WebKit. Поскольку определение имен рендеринговых движков является распространённым, многие строки пользователя добавили другие названия рендеринговых движков для запуска обнаружения. Поэтому важно обратить внимание, чтобы при обнаружении рендерингового движка не срабатывали ложные срабатывания.

Движок Должен содержать Примечание
Gecko Gecko/xyz
WebKit AppleWebKit/xyz Обратите внимание, браузеры WebKit добавляют строку «like Gecko», которая может вызвать ложное срабатывание для Gecko, если обнаружение не выполняется тщательно.
Presto Opera/xyz Примечание: Presto больше не используется в версиях Opera браузера >= 15 (см. «Blink»)
Trident Trident/xyz Internet Explorer помещает этот маркер в часть комментария строки пользователя
EdgeHTML Edge/xyz Не-Chromium Edge помещает версию движка после маркера Edge/, а не версию приложения. Примечание: EdgeHTML больше не используется в версиях браузера Edge >= 79 (см. «Blink»).
Blink Chrome/xyz

Версия рендерингового движка

Большинство рендеринговых движков помещают номер версии в маркер НазваниеДвижка/НомерВерсии, за исключением Gecko. Gecko помещает номер версии Gecko в часть комментария строки пользователя после строки rv:. Начиная с Gecko 14 для мобильной версии и Gecko 17 для настольной версии, это значение также помещается в маркер Gecko/version (предыдущие версии помещали туда дату сборки, а затем фиксированную дату, называемую GeckoTrail).

ОС

Операционная система указана в большинстве строк пользователя (хотя не в платформах, ориентированных на веб, таких как Firefox OS), но формат сильно варьируется. Это фиксированная строка между двумя точками с запятой в комментарии строки пользователя. Эти строки специфичны для каждого браузера. Они указывают ОС, но также часто ее версию и информацию об используемом оборудовании (32 или 64 бита, или Intel/PPC для Mac).

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

Мобильный, планшетный или настольный

Наиболее распространенная причина для выполнения анализа строк пользователя — определение типа устройства, на котором работает браузер. Цель — предоставить различный HTML для различных типов устройств.

  • Никогда не предполагайте, что браузер или рендеринговый движок работает только на одном типе устройств. Особенно не делайте различные значения по умолчанию для разных браузеров или рендеринговых движков.
  • Никогда не используйте маркер ОС для определения, является ли браузер мобильным, планшетным или настольным. ОС может работать на более чем одном типе (например, Android работает как на планшетах, так и на телефонах).

В следующей таблице обобщены способы, которыми распространённые поставщики браузеров указывают, что их браузеры работают на мобильном устройстве:

Браузер Правило Пример
Mozilla (Gecko, Firefox) Mobile или Tablet внутри комментария. Mozilla/5.0 (Android; Mobile; rv:13.0) Gecko/13.0 Firefox/13.0
WebKit-based (Android, Safari) Mobile Safari маркер вне комментария. Mozilla/5.0 (Linux; U; Android 4.0.3; de-ch; HTC Sensation Build/IML74K) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30
Blink-based (Chromium, Google Chrome, Opera 15+, Edge на Android) Mobile Safari маркер вне комментария. Mozilla/5.0 (Linux; Android 4.4.2); Nexus 5 Build/KOT49H) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/33.0.1750.117 Mobile Safari/537.36 OPR/20.0.1396.72047
Presto-based (Opera 12-) Opera Mobi/xyz маркер внутри комментария. Opera/9.80 (Android 2.3.3; Linux; Opera Mobi/ADR-1111101157; U; es-ES) Presto/2.9.201 Version/11.50
Internet Explorer IEMobile/xyz маркер в комментарии. Mozilla/5.0 (compatible; MSIE 9.0; Windows Phone OS 7.5; Trident/5.0; IEMobile/9.0)
Edge на Windows 10 Mobile Mobile/xyz и Edge/ маркеры вне комментария. Mozilla/5.0 (Windows Phone 10.0; Android 6.0.1; Xbox; Xbox One) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Mobile Safari/537.36 Edge/16.16299

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

Примечание: Если устройство достаточно велико, чтобы не быть отмеченным Mobi, вы должны предоставить свою настольную версию сайта (которая, как практика, должна поддерживать сенсорный ввод в любом случае, так как всё больше настольных компьютеров появляются с сенсорными экранами).

© 2005–2022 MDN contributors.
Licensed under the Creative Commons Attribution-ShareAlike License v2.5 or later.
https://developer.mozilla.org/en-US/docs/Web/HTTP/Browser_detection_using_the_user_agent

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API