Pull to refresh

simpleTooltip: HTML начиненный CSS и приправленный jQuery

Reading time 8 min
Views 33K


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

На разработку собственного варианта подсказок меня натолкнул Tipsy Tooltip используемый в Twitter Bootstrap. По началу я пользовался им, но все возможности плагина мне были не нужны и, будучи перфекционистом, лишний код смущал естество. Решил: напишу-ка я то, что нужно мне и ни строчкой больше. Написал и осмеливаюсь поделиться рецептом с сообществом. Авось кому-то приглянется…

Прочтите перед готовкой


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

Приготовим тесто — возьмем немного HTML


Тесто мы готовим часто и рецепт знаем на зубок — на этом этапе проделаем рутинную операцию: создадим HTML разметку со ссылками, каждая из которых будет иметь подсказку и, отличное от предыдущего, направление:

<section>
	<div>
		<a href="#" data-tooltip="nw" title="This is an example of Northwest gravity">Northwest</a>
	</div>
	<div>
		<a href="#" data-tooltip="north" title="This is an North gravity">North</a>
	</div>
	<div>
		<a href="#" data-tooltip="ne" title="This is an example of Northeast gravity">Northeast</a>
	</div>

	<div>
		<a href="#" data-tooltip="west" title="This is an example of West gravity">West</a>
	</div>
	<div>
		<h3>simpleTooltip</h3>
	</div>
	<div>
		<a href="#" data-tooltip="east" title="This is an example of East gravity">East</a>
	</div>

	<div>
		<a href="#" data-tooltip="sw" title="This is an example of Southwest gravity">Southwest</a>
	</div>
	<div>
		<a href="#" data-tooltip="south" title="This is an example of South gravity">South</a>
	</div>
	<div>
		<a href="#" data-tooltip="se" title="This is an example of Southeast gravity">Southeast</a>
	</div>
</section>

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

  • data-tooltip — смещение тела подсказки.

    • nw — северо-западное
    • north — северное
    • ne — северо-восточное
    • west — западное
    • east — восточное
    • sw — юго-западное
    • south — южное
    • se — юго-восточное

  • title — текст.

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

Начинка: проверьте, не закончился ли у вас CSS


Начинка должна быть вкусной, и по-этому мы не будем использовать никаких примесей и вредных веществ. Только, как его иногда называют, натурпродукт — pure CSS. Нам понадобятся псевдоэлементы :before и :after, селекторы атрибутов с модификаторами начала и конца строки (^, $) и CSS функция attr(). Убедившись в наличии ингредиентов можно приступать к готовке.

Запах

Мы ведь не хотим поедать дурно пахнущие тултипы? Точно не хотим и, по-этому, сразу говорим себе: никаких магических чисел — только обоснованный и осмысленный код.

Базовые стили

#tooltip_width {
	float:left;
}
#tooltip_width, [data-tooltip]:before, [data-tooltip]:after {
	display:none;
	font:normal 11px/24px Arial;
}
[data-tooltip]:before, [data-tooltip]:after {
	position:absolute;
	z-index:1000;
}
[data-tooltip]:before {
	content:attr(data-title);
	color:#fff;
	padding:0 5px;
	border-radius:2px;
	background:rgba(0,0,0,.8);
	text-align:center;
	white-space:nowrap;
}
[data-tooltip]:after {
	content:'';
	height:0;
	width:0;
	border:solid transparent;
	border-width:5px;
	pointer-events:none;
}
[data-tooltip]:hover:before, [data-tooltip]:hover:after {
	display:block;
}

Псевдоэлемент :after — декоративная стрелка-указатель на родителя, :before — тело тултипа с текстом. И вот еще что: вы точно заметили правила для элемента с #tooltip_width, но понятия не имеете зачем он нужен. Так вот он — это особая присыпка от повара и нужна она для измерения ширины подсказки. Изначально, на этапе составления представления о будущем блюде, планировалось рассчитывать ширину по формуле:

width = text.length * 5; // кол-во символов умноженное на среднюю ширину одного из них (Arial, 11px)

Но такой подход был обречен по двум причинам:
  • символы латиницы, в среднем, не так широки (~5px) как кириллические (~6.5px);
  • уравнение не предусматривало одинакового кол-ва символов разной ширины. Например "iii" не будут равны "WWW" по ширине.

Новый метод, учитывающий все особенности, работает следующим образом: текст подсказки помещается в невидимый, плавающий (float:left), блок с идентичными настройками кегля шрифта, затем измеряется его ширина и он удаляется из DOM, позволяя, тем самым, получить ширину следующего тултипа при помощи этого же элемента.

Селекторы атрибутов с модификаторами

Хороший выбор для группировки свойств. Можно обойтись и без них, как и кушать суп/борщ без хлеба, но все-таки с ними будет вкуснее.

Названия направления, такие как: nw и ne начинаются с латинской буквы n и имеют общие свойства — это играет нам на руку и мы можем описать их правила в таком виде:

[data-tooltip^=n]:before, [data-tooltip^=n]:after {
	top:100%;
}
[data-tooltip^=n]:before {
	margin-top:5px;
}
[data-tooltip^=n]:after {
	margin-top:-5px;
	border-bottom-color:rgba(0,0,0,.8);
}

Помним, что в псевдоэлементе :before располагается текст, а :after выступает декоративным указателем. Данный код работает для направлений nw, ne и north и смещает псевдоэлементы на 100% вниз: текст смещается еще на 5px ниже (5px — высота и половина ширины указателя), а стрелка настолько же выше — это позволяет «примагнитить» ее к телу тултипа.



Синяя линия — top:100%, краснаяmargin-top:5px.

Тоже самое и для sw, se и south, только смещение направлено вверх:

[data-tooltip^=s]:before, [data-tooltip^=s]:after {
	bottom:100%;
}
[data-tooltip^=s]:before {
	margin-bottom:5px;
}
[data-tooltip^=s]:after {
	margin-bottom:-5px;
	border-top-color:rgba(0,0,0,.8);
}

Как вы уже догадались, все названия для направления смещения подобраны так, чтобы их можно было легко объединять в группы. Давайте теперь опишем общие стили для nw, ne, sw, se, north и south с применением модификатора конца строки:

[data-tooltip$=w]:before, [data-tooltip$=w]:after, [data-tooltip$=th]:before, [data-tooltip$=th]:after {
	left:50%;
}
[data-tooltip$=w]:before {
	margin-left:-15px;
}
[data-tooltip$=w]:after, [data-tooltip$=th]:after {
	margin-left:-5px;
}
[data-tooltip$=e]:before, [data-tooltip$=e]:after {
	right:50%;
}
[data-tooltip$=e]:before {
	margin-right:-15px;
}
[data-tooltip$=e]:after {
	margin-right:-5px;
}



Синяя линия — right:50%, краснаяmargin-right:-5px, зеленаяmargin-right:-15px.

Теперь опишем поведение для двух оставшихся направлений — east и west:

[data-tooltip$=st]:before, [data-tooltip$=st]:after {
	bottom:50%;
}
[data-tooltip$=st]:after {
	margin-bottom:-5px;
}
[data-tooltip$=st]:before {
	margin-bottom:-12px;
}
[data-tooltip=west]:before, [data-tooltip=west]:after {
	left:100%;
}
[data-tooltip=west]:before {
	margin-left:10px;
}
[data-tooltip=west]:after {
	margin-right:-10px;
	border-right-color:rgba(0,0,0,.8);
}
[data-tooltip=east]:before, [data-tooltip=east]:after {
	right:100%;
}
[data-tooltip=east]:before {
	margin-right:10px;
}
[data-tooltip=east]:after {
	margin-left:-10px;
	border-left-color:rgba(0,0,0,.8);
}

В вышеуказанном коде может показаться странным следующий код: margin-bottom:-12px;. Отвечает он за смещение половины тела подсказки вверх. Почему 12 — половина? Потому что в самом начале мы указали высоту строки равной 24px при помощи следующего правила:

font:normal 11px/24px Arial; // обычное начертание Arial размером в 11px и высотой строки в 24px



Синяя линия — bottom:50%, краснаяmargin-bottom:-5px, зеленаяmargin-bottom:-12px.

Работа кипит: тесто набухло и ждет подоспевшую вовремя начинку. Бросаемся фаршировать и вспоминаем о присыпке без которой выглядеть наше блюдо будет не эстетично.

У вас же осталось немного jQuery с прошлой готовки?


По большому счету, тултипы можно нафаршировать и съесть уже сейчас, но перед тем как это сделать, вам нужно кое-что знать:

  • если в подсказке много текста, то она будет тянуться одной строкой на всю его ширину благодаря white-space:nowrap;
  • если у элемента с подсказкой статическое позиционирование (position:static;), которое имеют все элементы без определенного правила position, то подсказка не будет работать должным образом т.к. она позиционируется абсолютно элемента;
  • если направление тултипа задано как северное или южное, то он не будет на 100% соответствовать ему из-за абсолютного позиционирования и неявной ширины.

Исходя из этого, вы можете не использовать JavaScript тогда, когда уверены что:

  • элемент с подсказкой относительно или абсолютно позиционирован;
  • направление тултипа любое, кроме южного или северного;
  • текст подсказки не займет кучу пространства на экране.

А иначе, вот вам присыпка:

/**
*	@param (int) максимальная ширина подсказки.
*/
(function(){
	$.simpleTooltip = function(max){
		var max = max ? max : 300, body = $('body'), t = !1;

		$('[data-tooltip]').each(function(){

			t = $(this);

			t.attr('data-title', this.title).removeAttr('title');

			/**
			*	Все элементы, обладающие подсказкой, получают относительное позиционирование,
			*	но только в тех случаях, когда не используется абсолютное.
			*/
			if (t.css('position') == 'static') {
				t.css('position', 'relative');
			}

		}).on('mouseenter mouseleave', function(e){

			if (e.type == 'mouseenter') {

				t = $(this);

				/**
				*	При наведении мыши на элемент с подсказкой его текст помещается в плавающий блок
				*	и затем измеряется его ширина которая будет равна ширине подсказки.
				*/
				body.append('<div id="tooltip_width">'+ t.data('title') +'</div>');

				var width = $('#tooltip_width').width(), styles = '';

				/**
				*	Если ширина больше максимальной, то подсказка будет иметь ширину @max и получит
				*	особое форматирование текста.
				*/
				if (width > max) {
					width	=	max,
					styles	=	'[data-tooltip]:before{width:'+ width +'px;text-align:left;line-height:17px;padding:2px 5px;white-space:normal}';
				}

				/**
				*	Если подсказка направлена на юг или север, то она будет центрироваться таким образом:
				*	к ширине прибавляются боковые значения padding и вычитается половина, на которую и
				*	будет смещена подсказка относительно центра элемента, которому она дана.
				*/
				if (t.data('tooltip').slice(-2) == 'th') {
					styles	+=	'[data-tooltip$=th]:before{margin-left:-'+ ((width + 10) / 2) +'px}';
				}

				/**
				*	Если один из предыдущих, или оба, шагов пройдены, то в DOM будут добавлены стили
				*	подсказки для конкретного элемента. Иначе, если ширина меньше максимальной и 
				*	направление не южное или северное, то работа плагина закончится.
				*/
				if (styles) {
					body.append('<style id="tooltip_style">'+ styles +'</style>');
				}

			} else {

				$('#tooltip_style, #tooltip_width').remove();

			}

		});
	};
})(jQuery);

$(document).ready(function(){
	$.simpleTooltip();
});

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

$.simpleTooltip(400); // аргумент функции, 400 в нашем примере - максимальная ширина для всех тултипов.

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

Распространенность на кухнях мира


  • IE8 — с заменой RGBA формата цвета на HEX. Без закругленных углов *.
  • Остальные браузеры, поддерживающие CSS функцию attr() и псевдоэлементы, не познают бед.

* — в исходных файлах есть corners.htc, который заставит старые версии браузера от MS закруглять углы, но размер данного файла равен 4Кб, что на 1Кб больше размера файлов всего проекта без него.

От шеф-повара


Резюме:

Блюдо готово. Оно не экстравагантно и не эксцентрично. Это обыденная пища со своими преимуществами и недостатками и только вам решать чем отужинать. Но заметьте: не попробовав и не узнаешь нравится тебе что-то иль нет.

Ожидания:

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

Полезные материалы



Возьмите готовое


Версия 1.0 c поддержкой IE8* или 1.0.1 без нее.

* — в версии 1.0 необходимо помещать текст подсказки в атрибут data-title.

Употребляйте на здоровье!
Tags:
Hubs:
+24
Comments 37
Comments Comments 37

Articles