Xiper

Определение браузера и его версии

Автор: Андрей Косяк и Евгений Рыжков Дата публикации:

Определение браузера и его версии 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, если не совпадает

Скачать всё целиком можно тут:

Как использовать

  1. подключаем к странице вышеприведенный скрипт
  2. вызываем функцию getBrowser (например, при загрузке страницы — событие onload)
  3. условием отслеживаем нужный браузер, при необходимости и нужную версию, и выполняем какой-то ко для него

Варианты имен браузеров, которые возращает функция:

  • 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 было замечено неадекватное восприятие комментариев, поэтому, если вдруг он не захочет "определятся", рекомендую взять версию скрипта без комментариев

Обновление ver 1.1

Недавний проект натолкнул на мысль, которая "обсасывается" в сети уже долгое время и имеет как уйму сторонников, так и противников. А именно: просить(требовать) юзера обновить браузер для адекватного отображения сайта. Решений и идей по этому вопросу сегодня существует масса, каждый волен выбрать себе более подходящую сам. Я же предлагаю небольшое дополнение к плагинчику bdetect.

Просим обновиться

Не всегда трудности нам приносит наш "горячо любимый ИЕ6", есть масса(а современем она только увеличивается) приёмов и css-правил(CSS3), кои уже можно успешно применять глядя на актуальные версии браузеров, но есть люди которым лень или некогда обновить свой браузер(я говорю сейчас об Opera, Mozzila и т.п), более того, даже у заказчиков порой обнаруживаются старые версии(недавно вычислилась Opera древнее 10.10)... Но ближе к "телу", как говорится, итак функция checkBrowser():

/*
checkBrowser(params = {
     warning: [string],		// окончание шаблонной фразы "Версия Вашего браузера BBBBB (ver ХХ.ХХ) {warning}"
     message: [string],		// основное сообщение
     question: [string],	// сам вопрос "Не желаете ли?" (у каждого свой подход к людям) :)
     chrome: [float],		// минимально-допустимые версии браузеров
     safari: [float],			 
     msie: [float],			 
     opera: [float],
     firefox: [float],
     chromeLink: [string],	// ссылки на обновление
     safariLink: [string],
     msieLink: [string],
     operaLink: [string],
     firefoxLink: [string]
})
*/
function checkBrowser(){
	if (!params.chromeLink) params.chromeLink = '';
	if (!params.safariLink) params.safariLink = '';
	if (!params.msieLink) params.msieLink = '';
	if (!params.operaLink) params.operaLink = '';
	if (!params.firefoxLink) params.firefoxLink = '';
	if (!params.warning) params.warning = 'устарела!rnrn'
		else params.warning += 'rnrn';
	if (!params.question) params.question = 'Хотите ли обновить версию своего браузера?';
	if (!params.message) params.message = ''
		else params.message += 'rnrn';
	if (!params.chrome) params.chrome = 6;
	if (!params.safari) params.safari = 5;
	if (!params.msie) params.msie = 6;	
	if (!params.opera) params.opera = 10.5;
	if (!params.firefox) params.firefox = 3.5;
var 	browser = getBrowser(1),
	browserName = browser[0],
	browserVer = browser[1],
	browserVerPoints = browser[2],
	browserVer = parseFloat(browserVer+"."+browserVerPoints),
	systemMessage = 'Версия Вашего браузера '+browserName+'(ver '+browserVer+') '+params.warning+params.message+params.question,
	bLink = '';
switch (browserName)
{
	case 'Chrome':
		if (browserVer >= params.chrome) return false
			else bLink = params.chromeLink; break;
	case 'Safari':
		if (browserVer >= params.safari) return false
			else bLink = params.safariLink; break;
	case 'MSIE':
		if (browserVer >= params.msie) return false
			else bLink = params.msieLink; break;
	case 'Opera':
		if (browserVer >= params.opera) return false
			else bLink = params.operaLink; break;
	case 'Firefox':
		if (browserVer >= params.firefox) return false
			else bLink = params.firefoxLink; break;
};
		var conf = confirm(systemMessage);
		if (conf == true) document.location.href = bLink
			else return false;
};		

Ничего супер особенного, функция, предлагает обновить браузер, если его версия ниже минимально-допустимой. По умолчанию, минимальные версии установлены такие:

  • Chrome 6
  • Safari 5
  • IE 5.5
  • Opera 10.5
  • Firefox 3.5

Линки на обновление:

А также фраза по умолчанию: "Версия Вашего браузера {имя браузера}(ver {версия}) устарела! Хотите ли обновить версию своего браузера?"

Все вышеперечисленное будет справедливо при вызове "голой" функции: checkBrowser().

Результат(я специально завысил минимальные версии, дабы увидеть диалог)

Своя рубаха...

Поскольку каждая верстка уникальна и у каждого web-разработчика своё представление о вежливости и корректности, то любой из параметров можно задать при вызове функции, я задам все:

checkBrowser (params = {
			warning: 'уже немного несвежая!',
			message: 'Так как Ваш браузер самый лучший и быстрый, то Вы можете сделать его еще лучше и быстрее.',
			question:'Не хотите ли Вы, многоуважаемый юзер, обновить свой, безспорно, лучший браузер в мире?',
			chrome: 10,
			safari: 10,
			msie: 9,
			opera: 11,
			firefox: 4		
});

Результат

Скачать:

  • - 5кб
  • - 2кб