HTML5 <meter> таг

В урока си Разлики в HTML4 и HTML5 споменавам <meter> тага, казвайки, че той няма вградена визуална презентация в браузърите. След бърза справка се оказа, че греша и повечето браузъри вече представят този таг като правоъгълник запълнен до определено ниво. В този урок ще дам няколко насоки как да използваме най-правилно този таг и как да се уверим, че той ще изглежда правилно и на по-стари браузъри.

Начален set-up

Нека започнем със структурата на една съвсем елементарна страница, съдържаща <meter> таг:

<!DOCTYPE html>
<html>
<head>
	<meta charset="utf-8" />
	<title>meter tag</title>
	<link rel="stylesheet" type="text/css" href="style.css">
	<script type="text/javascript" src="jquery-1.8.3.min.js"></script>
	<script type="text/javascript" src="func.js"></script>
</head>

<body>
	<h1>Колко е 3/10:</h1>
	
	<meter id="progress" min="0" max="100" value="30" title="percents">30%</meter>
</body>
</html>

Стандартни HTML5 Doctype, UTF-8 charset, заглавие, stely.css файл, който ще добавим след малко, включване на jQuery 1.8.3 и func.js файл, който също ще добавим в последствие.

В <body>-то имаме заглавие и <meter>. На <meter>-а сме задали id, min, max, value и title атрибути. min и max може би се досещате, че показват съответно минималната и максималната стойност на нашия <meter>. Най-често минималната стойност би била 0, но при по-особени обстоятелства може да е различна. Например минималната оценка в училище е 2. Важното е обаче тези минимум и максимум да са числа.

value пък изразява текущата стойност и до там ще бъде запълнен нашият <meter>.

title упоменава мерната единица.

Нека сега добавим някакви основни стилове към страницата:

@charset "utf-8";

* { margin: 0; padding: 0; }
html, body { width: 100%; height: 100%; }

body { background: #E0E0E0; color: #000; font: normal 12px Arial, Helvetica, sans-serif; }

h1 { padding-top: 30px; }
h1, #progress { font-size: 250%; text-align: center; }
#progress { display: block; width: 400px; height: 30px; margin: 20px auto 30px; border: 1px solid #B0B0B0; }

Начален резултат

Ето какво имаме за сега при най-популярните браузъри:

meter таг в различните браузъри

Както виждате Chrome, Firefox и Opera имат поддръжка за визуализация на тага. Дори и зеленият цвят на запълване е почти еднакъв (само на Opera е по-светъл). Разликите са, че Chrome и Firefox добавят и светлосенки.

Проблемът е, че при Internet Explorer (дори версия 10) и Safari нещата не изглеждат толкова приятно. За да накараме нашия meter да изглежда сносно и там трябва да използваме малко JavaScript, с който да направим няколко

DOM манипулации

Идеята тук е следната: да направим един <div> контейнер за цялостния <meter>, като вътре в него се съдържа друг <div>, който да отговаря за запълването.

Трябва обаче да добавим и следните редове към нашите стилове:

#progress { position: relative; }
#progress .value { width: 0; height: 100%; margin: 0 auto 0 0; background: #90BE29; }

Задаваме margin: 0 auto 0 0;, защото на IE7 div.value се центрира в parent container-а си с равни по стойност margin-и от ляво и дясно.

Забележка: макар и в този случай да използваме DIV тагове, въпросните промени могат да се направят и с други (например <span>), в случай, че вашият <meter> е поставен в inline елемент.

Ето го и JavaScript-а който ще добавим, а по-долу следват и обяснения:

var newTags = ['meter'];

for (var t in newTags) {
	document.createElement(newTags[t]);
}

jQuery(function ($) {
	// !document.createElement('meter').hasOwnProperty('min') -- not working in Firefox
	if (document.createElement('meter')['min'] == undefined) {
		$('meter').each(function () {
			var divMeter = $('<div id="' + $(this).attr('id') + '"><div class="value"></div></div>');
			
			var value = ( $(this).attr('value') - $(this).attr('min') ) / ( $(this).attr('max') - $(this).attr('min') );
			
			divMeter.find('.value').css('width', value * 100 + '%');
			
			$(this).replaceWith(divMeter);
		});
	}
});

В масива newTags записваме списък от всички HTML5 тагове, които използваме, за да добавим поддръжка за тях в IE7/8. По принцип може би бихте желали да си имате порция код, която да добавя всички HTML5 тагове, но за да опростя примера съм добавил само meter.

Продължаваме с функция, която се изпълнява на DOMContentLoaded. В тази функция първо правим проверка дали текущият браузър поддържа <meter> тага. Подобна проверка можем да направим за всеки един таг, като създадем нова инстанция програматично от JavaScript по следния начин:

var meter = document.createElement('meter');

Вместо “meter” може да се използва името на всеки друг таг.

След като сме създали нова инстанция можем да направим проверка дали този нов обект поддържа property-та характерни за него. Тъй като <meter> тагът поддържа атрибут min можем да направим проверка дали като DOM елемент той има property min по следния начин:

var meter = document.createElement('meter');
var lacksSupport = !meter.hasOwnProperty('min');

Тествайки този метод обаче срещнах проблеми с Firefox, затова съм закоментирал тази проверка и съм опитал по друг начин:

var lacksSupport = document.createElement('meter')['min'] == undefined;

Тук отново създаваме нова инстанция, но директно взимаме нейното property min и го сравняваме с недефинираната променлива undefined.

undefined

Този момент с недефинираната променлива е малко тънък, но може да го срещнете на много места, затова ето няколко думи по въпроса:

Още в един от началните уроци за JavaScript говорихме, че променливите си имат тип. Този тип може да се достъпи с typeof(varName). Ако опитаме да вземем типа на променлива, която не е дефинирана, резултатът от функцията typeof() ще бъде 'undefined'. Нарочно изписвам резултата в кавички, като по този начин наблягам, че резултатът сам по себе си е string, но стойността му е “undefined”.

В JavaScript няма запазена дума undefined, така че можем да създадем такава променлива. От друга страна, ако не сме я създали, а се опитаме да достъпим типа и чрез typeof(), то бихме получили резултат 'undefined'.

В примера по-горе можем да използваме:

var lacksSupport = typeof(document.createElement('meter')['min']) == 'undefined';

но сме избрали по-кратък вариант, в който да пропуснем typeof и да проверим дали това property (min) липсва така както липсва дефиниция за променливата undefined.

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

Задвижване на meter на IE

Да се върнем сега на <meter>-а.

След като сме направили проверка дали браузърът не поддържа функционалността на този таг изпълняваме едно кратко заместване на всички места, където тагът се среща в markup-а.

Създаваме нашия контейнер с вложен <div>, който да изразява стойност, като му задаваме ID, което да е същото като на <meter>-а, който заменяме:

var divMeter = $('<div id="' + $(this).attr('id') + '"><div class="value"></div></div>');

След това изчисляваме стойността на <meter>-а в проценти и използваме тези проценти, за да променим широчината на запълващия div.value.

var value = ( $(this).attr('value') - $(this).attr('min') ) / ( $(this).attr('max') - $(this).attr('min') );

divMeter.find('.value').css('width', value * 100 + '%');

Накрая просто заместваме markup-а на <meter>-а с този, който сме подготвили в променливата divMeter:

$(this).replaceWith(divMeter);

Можете да видите крайния вариант на https://magadanski.com/demo/meter/

Ако искате <meter>-а да се вижда еднакво на всички браузъри можете да премахнете проверката за поддръжка на тага и JavaScript-ът да се изпълни за всички браузъри, в следствие на което роля ще оказват само стиловете, които сте добавили за допълнителните тагове.

Вашият коментар

Вашият имейл адрес няма да бъде публикуван. Задължителните полета са отбелязани с *

Този сайт използва Akismet за намаляване на спама. Научете как се обработват данните ви за коментари.