Определение браузера и его версии
Дата публикации: 02.06.2010
Internet Explorer, Firefox, Opera, Chrome, Safari, Netscape, Konqueror, Avant, Dillo… и это только некоторые из ныне доступных браузеров пользователю. Что же в этом плохого?
В «идеальном» мире есть утвержденные веб стандарты, которых придерживаются все разработчики браузеров. Это значит, что на один и тот же правильно написанный тобою HTML код (или CSS код, или JS), каждый из браузеров реагирует одинаково. Но в реальном мире у разработчиков браузеров преобладает мнение, что стандарты — это для далекого розового будущего и когда оно придет они начнут их придерживаться. А сейчас иногда браузеры позволяют совершенно непредсказуемо реагировать на код.
Тебе остается для таких «иногда» научиться точно определять браузер и применять к нему особые меры.
Задача
При помощи JavaScript определить браузер и его версию (без использования дополнительных библиотек).
Метод навигатора
Теория
Среди объектов javascript есть объект navigator, у которого доступен метод UserAgent. Этот метод вернет строку, в которой содержится информация о браузере, его версии, ядре, операционной системе, в которой он запущен, а так же некоторых агентах и службах, встроенных в него. Примеры UserAgent:
- Opera/9.64 (Windows NT 5.1; U; ru) Presto/2.1.1
- Mozilla/5.0 (X11; U; Linux i686; it-IT; rv:1.9.0.2) Gecko/2008092313 Ubuntu/9.25 (jaunty) Firefox/3.8
- Mozilla/5.0 (Windows; U; MSIE 7.0; Windows NT 6.0; el-GR)
Решение
Т.к. UserAgent возращает имя браузера и его версию для разных браузеров однотипно, поэтому можно составить регулярное выражение для получения нужной информации. Допустим UserAgent вернул такую строку:
Opera/9.64 (Windows NT 5.1; U; ru) Presto/2.1.1
В этой строке нужная информация — это Opera/9.64. Парсим строку и «вытаскиваем» нужное:
function browserDetectNav(chrAfterPoint) { var UA=window.navigator.userAgent, // содержит переданный браузером юзерагент //-------------------------------------------------------------------------------- OperaB = /Opera[ /]+w+.w+/i, // OperaV = /Version[ /]+w+.w+/i, // FirefoxB = /Firefox/w+.w+/i, // шаблоны для распарсивания юзерагента ChromeB = /Chrome/w+.w+/i, // SafariB = /Version/w+.w+/i, // IEB = /MSIE *d+.w+/i, // SafariV = /Safari/w+.w+/i, // //-------------------------------------------------------------------------------- browser = new Array(), //массив с данными о браузере browserSplit = /[ /.]/i, //шаблон для разбивки данных о браузере из строки OperaV = UA.match(OperaV), Firefox = UA.match(FirefoxB), Chrome = UA.match(ChromeB), Safari = UA.match(SafariB), SafariV = UA.match(SafariV), IE = UA.match(IEB), Opera = UA.match(OperaB); //----- Opera ---- if ((!Opera=="")&(!OperaV=="")) browser[0]=OperaV[0].replace(/Version/, "Opera") else if (!Opera=="") browser[0]=Opera[0] else //----- IE ----- if (!IE=="") browser[0] = IE[0] else //----- Firefox ---- if (!Firefox=="") browser[0]=Firefox[0] else //----- Chrom ---- if (!Chrome=="") browser[0] = Chrome[0] else //----- Safari ---- if ((!Safari=="")&&(!SafariV=="")) browser[0] = Safari[0].replace("Version", "Safari"); //------------ Разбивка версии ----------- var outputData; // возвращаемый функцией массив значений // [0] - имя браузера, [1] - целая часть версии // [2] - дробная часть версии if (browser[0] != null) outputData = browser[0].split(browserSplit); if ((chrAfterPoint==null)&&(outputData != null)) { chrAfterPoint=outputData[2].length; outputData[2] = outputData[2].substring(0, chrAfterPoint); // берем нужное ко-во знаков return(outputData); } else return(false); }
В функцию передаётся всего один параметр — chrAfterPoint, который определяет количество возвращаемых знаков версии. Т.е. функция вернет номер версии и chrAfterPoint знаков после запятой. Если параметр не будет передан — функция вернет все знаки после запятой.
Пример вызова функции:
<button onclick="showBrowVer()">Определить браузер</button> <script type="text/javascript"> // скриптик будет вызывать нашу функцию определения // и выводить результат function showBrowVer() { var data = browserDetectNav(); //вызываем функцию, параметр НЕ передаем, //поэтому в результате получим все знаки версии после запятой alert("Браузер: "+data[0]+", Версия: "+data[1]+"."+data[2]); //выводим результат } </script>
Демо пример. Проверено в:
Недостаток
Это информация из UserAgent. Его очень легко подделать, подменить или, как говориться, «подсунуть»(такой возможностью наделены такие звери как Opera и Safari). К тому же зачастую менее известные браузеры представляются более именитыми «коллегами», но это совсем не значит, что они поддерживают нужные нам свойства или методы.
Вывод
Несмотря на недостаток, такой метод в большинстве случаев определит браузер совершенно точно, а если в него интегрировать еще и парочку способов по вычислению «партизанов» (маскирующихся браузеров), то доля удачных определений существенно возрастёт.
Заметки
- в примере определяются самые популярные браузеры (IE, Firefox, Opera, Chrome и Safari), остальные можно добавить по желанию и потребностям;
- нужно заметить, что Safari и Opera (начиная с 10-й версии) передают информацию о себе двумя блоками в строке Useragent, поэтому для них схема отличается от общей
- т.к. Opera умеет »прикидываться« другими браузерами, казалось бы, что её по юзерагенту не вычислить, тем не менее в строке юзерагента помимо идентификатора другого браузера, она передаёт и свой. Проверку на Opera просто ставим в первую очередь.
Метод объектов и свойств
Усложним задачу так, чтобы скрипт научился обнаруживать и «партизан» :). Проще говоря, определить браузер однозначно, даже если он умеет маскироваться под своих коллег.
Теория
После некоторого анализа и не без помощи Google, было выяснено следующее:
-
Opera
Умеет(умела) прикидываться другими браузерами. Но тем не менее, объект JavaScript window.opera поддерживают все версии оперы 5+. Вот она и спалилась. Кстати, этот же объект может выдать нам и версию: window.opera.version().
-
Chrome
Так же имеет уникальный для себя объект window.chrome
-
Firefox
C давних пор имеет, как минимум, объект window.sidebar и window.globalStorage, которые не поддерживают другие браузеры.
-
Safari
Не легко его отделить от всех браузеров, тем не менее обнаружилось, что такой объект как window.external в Safari отсутствует (кстати, также его нет в Opera, но это не страшно).
-
IE
Линейку IE выделить не составило большого труда. Такие объекты как window.all и window.ActiveXObject поддерживает только IE. Другой вопрос, как его разбить по версиям? Но и тут есть варианты:
-
IE 6
Такое свойство как window.navigator.userProfile поддерживает только он
-
IE 8
Появился объект window.Storage и window.Event, коих не было в предыдущих версиях.
-
IE 7
Можно выделить методом исключения. А именно: по объекту window.Event или window.Storage, которые характерны для 8-й версии и все тому же свойству window.navigator.userProfile, которое характерно для 6-й.
-
Решение
Путём проверки на поддержку определенного объекта (свойтсва, метода), делаем вывод о браузере, а в некоторых случаях и о его версии (Opera, IE).
Пример функции:
function browserDetectJS() { var browser = new Array(); //Opera if (window.opera) { browser[0] = "Opera"; browser[1] = window.opera.version(); } else //Chrome if (window.chrome) { browser[0] = "Chrome"; } else //Firefox if (window.sidebar) { browser[0] = "Firefox"; } else //Safari if ((!window.external)&&(browser[0]!=="Opera")) { browser[0] = "Safari"; } else //IE if (window.ActiveXObject) { browser[0] = "MSIE"; if (window.navigator.userProfile) browser[1] = "6" else if (window.Storage) browser[1] = "8" else if ((!window.Storage)&&(!window.navigator.userProfile)) browser[1] = "7" else browser[1] = "Unknown"; } if (!browser) return(false) else return(browser); }
Демо пример. Проверено в:
Недостатки
Путём проверки поддержки объектов можно сделать вывод о марке практически любого браузера, но не всегда можно определить версию. Поэтому, если использовать такой способ, то там, где вывод о версии невозможно сделать на основании объектов, придётся брать версию из того же UserAgent.
Заметки
- Конечно же, уникальных объектов для разных браузеров существует порядком больше, но на данный момент этого будет достаточно, дабы пробить браузер на поддерживаемые объекты.
Комплексный подход
Складываем оба метода в один файл, пишем «объединяющую» функцию с несколькими условиями и получаем на выход мини-библиотечку с тремя способами определения.
Пример объединяющей функции:
/* Набор функций для определения имени и версии браузера. Функция browserDetectNav - определение браузера при помощи объекта Navigator Функция browserDetectJS - определение браузера при помощи JS объектов и св-в Функция getBrowser - делает вывод о браузере на основании обоих методов Формат входящих и исходящих данный у всех функций одинаков. Методы не зависимы друг от друга, можно использовать любой или вместе. Входящие параметры: chrAfterPoint - целое число, определяющее кол-во знаков после запятой в возвращаемой версии браузера. Если оставить пустым - вернет все знаки после запятой. Возвращаемые параметры: outputData - массив, где outputData[0] - имя браузера, outputData[1] - основная версия браузера (значение до запятой), outputData[2] - знаки версии после запятой (кол-во определяется входящим параметром) если возвращается "undefined" - браузер (или версия) не определен (неизвестный) если не возвращается версия (в некоторых случаях) - браузер "маскированый" */ function browserDetectNav(chrAfterPoint) { var UA=window.navigator.userAgent, // содержит переданный браузером юзерагент //-------------------------------------------------------------------------------- OperaB = /Opera[ /]+w+.w+/i, // OperaV = /Version[ /]+w+.w+/i, // FirefoxB = /Firefox/w+.w+/i, // шаблоны для распарсивания юзерагента ChromeB = /Chrome/w+.w+/i, // SafariB = /Version/w+.w+/i, // IEB = /MSIE *d+.w+/i, // SafariV = /Safari/w+.w+/i, // //-------------------------------------------------------------------------------- browser = new Array(), //массив с данными о браузере browserSplit = /[ /.]/i, //шаблон для разбивки данных о браузере из строки OperaV = UA.match(OperaV), Firefox = UA.match(FirefoxB), Chrome = UA.match(ChromeB), Safari = UA.match(SafariB), SafariV = UA.match(SafariV), IE = UA.match(IEB), Opera = UA.match(OperaB); //----- Opera ---- if ((!Opera=="")&(!OperaV=="")) browser[0]=OperaV[0].replace(/Version/, "Opera") else if (!Opera=="") browser[0]=Opera[0] else //----- IE ----- if (!IE=="") browser[0] = IE[0] else //----- Firefox ---- if (!Firefox=="") browser[0]=Firefox[0] else //----- Chrom ---- if (!Chrome=="") browser[0] = Chrome[0] else //----- Safari ---- if ((!Safari=="")&&(!SafariV=="")) browser[0] = Safari[0].replace("Version", "Safari"); //------------ Разбивка версии ----------- var outputData; // возвращаемый функцией массив значений // [0] - имя браузера, [1] - целая часть версии // [2] - дробная часть версии if (browser[0] != null) outputData = browser[0].split(browserSplit); if ((chrAfterPoint==null)&&(outputData != null)) { chrAfterPoint=outputData[2].length; outputData[2] = outputData[2].substring(0, chrAfterPoint); // берем нужное ко-во знаков return(outputData); } else return(false); } function browserDetectJS() { var browser = new Array(); //Opera if (window.opera) { browser[0] = "Opera"; browser[1] = window.opera.version(); } else //Chrome if (window.chrome) { browser[0] = "Chrome"; } else //Firefox if (window.sidebar) { browser[0] = "Firefox"; } else //Safari if ((!window.external)&&(browser[0]!=="Opera")) { browser[0] = "Safari"; } else //IE if (window.ActiveXObject) { browser[0] = "MSIE"; if (window.navigator.userProfile) browser[1] = "6" else if (window.Storage) browser[1] = "8" else if ((!window.Storage)&&(!window.navigator.userProfile)) browser[1] = "7" else browser[1] = "Unknown"; } if (!browser) return(false) else return(browser); } function getBrowser(chrAfterPoint) { var browserNav = browserDetectNav(chrAfterPoint), // хранит результат работы функции browserDetectNav browserJS = browserDetectJS(); // хранит результат работы функции browserDetectJS // сравниваем результаты отработки двух методов if (browserNav[0] == browserJS[0]) return(browserNav) //если одинаковый - возвращаем результат первого метода else if (browserNav[0] != browserJS[0]) return(browserJS) //если разный - возвращаем результат второго метода else return(false); //в случае, если браузер не определен }; function isItBrowser(browserCom, browserVer, detectMethod) { var browser; // контейнер для результатов обнаружения switch (detectMethod) { // определяемся какой из методов использовать (3-й параметр) case 1: browser = browserDetectNav(); break; case 2: browser = browserDetectJS(); break; default: browser = getBrowser(); }; if ((browserCom == browser[0])&(browserVer == browser[1])) return(true) // если переданы два параметра и они совпали - возвращаем true else if ((browserCom == browser[0])&((browserVer == null)||(browserVer == 0))) return(true) // если передан один параметр и он совпал - возвращаем true else return(false); };
Итого имеем:
- browserDetectNav() — определяем браузер методом «Навигатора»
- browserDetectJS() — определяем браузер методом JS объектов и свойств
- getBrowser() — определяя браузер, прмиеняем сразу два метода для большей надежности
- isItBrowser(browserName, [version]) — функция вернет true, если текущий браузер совпадает с указанным и false, если не совпадает
Скачать всё целиком можно тут:
Как использовать
- подключаем к странице вышеприведенный скрипт
- вызываем функцию getBrowser (например, при загрузке страницы — событие onload)
- условием отслеживаем нужный браузер, при необходимости и нужную версию, и выполняем какой-то ко для него
Варианты имен браузеров, которые возращает функция:
- MSIE
- Safari
- Chrome
- Opera
- Firefox
Например, хотим «поймать» IE7:
<script type="text/javascript" src="path-to/bdetect.js"></script> <script type="text/javascript"> function bdetect() { getBrowser(); /* ставим условие, в котором определяем нужный нам браузер и его версию */ if(data[0]=="MSIE" && data[1]=="7") { какой-то код для IE7 } } window.onload = bdetect; </script>
или можно вот так:
<script type="text/javascript" src="path-to/bdetect.js"></script> <script type="text/javascript"> function bdetect() { if(isItBrowser("MSIE","7")) { какой-то код для IE7 } } window.onload = bdetect; </script>
Вывод
Если использовать оба метода в совокупности (вызывая функцию getBrowser), то через такой «фильтр» вероятность «просочиться» крайне мала, да и на практике не так часто приходится столь жестко придираться к бедным браузерам.
Заметки
- за ИЕ6 было замечено неадекватное восприятие комментариев, поэтому, если вдруг он не захочет «определятся», рекомендую взять версию скрипта без комментариев