Tabs – част 2 – Deep Linking

В предния урок показах как можем с помощта на jQuery UI да добавим tabs widget в някоя HTML страница. Това, което ще добавим сега е deep linking за отделните табове.

Като за начало аз лично съм сменил съдържанието на отделните табове, за да разграничаваме по-лесно, че в момента сме на различен таб.

В наша подкрепа се явява факта, че jQuery Tabs Widget-а е написан достатъчно добре, така че ако URL-ът на страницата съдържа ?tab=#ID, widget-ът сам ще потърси дали няма панел, чието id да е ID. Ако такъв панел съществува – той автоматично ще бъде показан. Така няма да ни се налага на нас да викаме методи, които да го активират.

Така задачата ни се свежда до добавяне на „tab“ GET агрумент в URL-а на страницата. Стойност трябва да е „#“ последван от id-то на панела. В общи линии се получава CSS селектор по ID.

Автоматично засичане с rel

Първият начин да постигнем това е с rel атрибут със синтаксис, който jQuery Address да отчете. Промяната се свежда до елементарен update на markup-а ни:

<div id="tabs">
	<ul>
		<li><a href="#first" rel="address:/tabs.html?tab=#first">First</a></li>
		<li><a href="#second" rel="address:/tabs.html?tab=#second">Second</a></li>
		<li><a href="#third" rel="address:/tabs.html?tab=#third">Third</a></li>
	</ul>
	
	...
</div>

Ако стойността на rel атрибута на някой линк започва с „address:“, то jQuery Address ще се погрижи address bar-ът на браузъра ни да се update-не до стойността, която сме изписали след това. В нашия случай това трябва да съдържа „tabs.html“. Това е защото предварително сме задали основата на URL адреса, с която jQuery Address работи, да е root адреса на страницата ни. Той не включва адреса на страницата tabs. След това трябва да добавим въпросителен знак, отделящ GET аргументите в адреса. Накрая трябва да зададем и двойката property-value за GET аргумента, който искаме да добавим.

Проблеми при употребата на rel

Макар и това да е най-лесният начин, за който се сещам в момента, на мене не ми харесва да задавам произволни стойности в rel атрибут. По спецификация, този атрибут може да съдържа точно определени стойности. Нашият адрес не влиза в тях.

Друга причина е, че при активен tab, линковете за табовете получават активно състояние и с използване на стрелките за наляво и надясно можем да променим активния таб. Това е част от accessibility възможностите на jQuery UI Tabs. При промяна на активния tab по този начин обаче, ние не кликаме на линка. Съответно jQuery Address не обновява стойността на адреса на браузъра.

На всичко отгоре на мене лично не ми харесва да трябва да изписвам името на страницата (tabs.html).

По тази причина бих предпочел да направя обновяването чисто с JavaScript.

Засичане на смяна на таб

Това, което трябва да направим е по някакъв начин да засечем кога табовете се сменят и в този момент да обновим адреса на браузъра. Засичането на смяната на табовете можем да извършим като използваме event-ите на jQuery UI Tabs widget-а. Ако потърсите в интернет лесно можете да намерите пълната документация на jQuery UI Tabs Widget. В секцията events ще видите acivate, който е event, изпълняващ се при активиране на нов таб. Точно това ни трябва и на нас.

Handler-и за jQuery UI Widget-и се задават като property-та на инициализационен обект:

$('#tabs').tabs({
	activate: function (e, ui) {
		// add deep linking state
	}
});

От документацията виждаме, че при извикването на функцията се подават два параметъра – event и ui. Първият съдържа основна информация за самия activate event. Вторият съдържа информация относно самия widget. Тя включва кой е бил предишно активният таб, кой е новият активен и т.н.

Като стойност на property-то подаваме функция. Не забравяйте, че в JavaScript функциите са просто тип обект. В нея трябва да добавим нашия код, грижещ се за правилното обновяване на адреса на браузъра.

По принцип следния код би трябвало да работи:

$('#tabs').tabs({
	activate: function (e, ui) {
		$.address.parameter('tab', '#' + ui.newPanel.attr('id'));
	}
});

Ръчно задаване на GET аргумент

Това, което правим е, че при активиране на нов таб задаваме parameter (GET аргумент) със ключ „tab“ и стойност „#tabID“. Където „tabID“ е id-то на новия панел, който сме достъпили чрез аргумента ui.

Проблемът идва в това, че jQuery Address не засича правилно стойността на GET аргументи, които съдържат „#“. В следствие от което кодът се чупи. Ако в бъдеще този bug бъде поправен, то кодът ни ще функционира правилно. До тогава обаче трябва да намерим друго решение.

Можем да опитаме да подадем само стойността на ID-то на панела, без този „#“. Тогава пък възниква конфликт с jQuery UI Tabs, който не може да изпълни правилно селектора, без „#“.

При опит да заменим селектора с div#ID jQuery UI Tabs работи правилно, но jQuery Address отново се чупи, поради наличието на „#“, макар и не в началото на параметъра.

Налага се да напишем малко повече код:

$('#tabs').tabs({
	activate: function (e, ui) {
		var currAddress = $.address.value();
		var tab = '#' + ui.newPanel.attr('id');
		var newAddress = '';
		
		if (currAddress.match(/\?/)) {
			var parameters = $.address.parameterNames();
			if ($.inArray('tab', parameters) == -1) {
				newAddress += '&tab=' + tab;
			} else {
				newAddress = currAddress.replace(/tab=#[^&]+/, 'tab=' + tab);
			}
		} else {
			newAddress = currAddress + '?tab=' + tab;
		}
		
		$.address.value(newAddress);
	}
});

Активиране на tab-а

Първо записваме текущия адрес в променливата currAddress за по-лесно достъпване в последствие.

След това в променливата tab се записваме стойността на GET аргумента, който трябва да добавим.

Създаваме празна променлива newAddress, в която по-късно ще запишем как трябва да изглежда новия адрес.

Правим проверка дали текущият адрес съдържа „?“ (дали вече има GET аргументи). След малко ще обясня какво правим ако има, нека само за момент прескочим до else-а. Ако няма GET аргументи – животът ни се улеснява и просто добавяме ?tab=#tabID към текущия адрес.

Ако обаче имаме GET аргументи първо взимаме списък с ключовете им чрез $.address.parameterNames(). След това правим проверка дали сред тези GET аргументи фигурира нашия. Тогава трябва да го презапишем. Или да проверим дали тези GET аргументи са други и трябва да добавим нашия.

Колкото и елементарно да звучи, резултатът от $.inArray() е доста хлъзгав. Функцията приема търсения елемент като първи аргумент и масив с елементи, в който да търсим като втори аргумент. Функцията връща като резултат или индексът на елемента (ако е намерен) с 0-лево индексиране, или „-1“, ако елементът липсва. Макар и да знам това, пишейки урока аз лично допуснах грешката да очаквам булева стойност като резултат от функцията. Тъй като елементът беше първи в масива (индекс 0), условието не се изпълняваше, тъй като 0 се счита за равно на false, която беше в разрез с „правилния“ за мене резултат.

Ако GET аргументът „tab“ не присъства в текущите GET аргументи, тогава просто добавяме &tab=#tabID към адреса. В противен случай, с помощта на регулярен израз заменяме стойността на аргумента с ID-то на новия таб.

Обяснение на регулярния израз

Ето и малко разяснения за регулярния израз:

  1. Започваме засичането с обикновения текст tab=#.
  2. След това в квадратни скоби изреждаме знаци, които допускаме, че може да се съдържат
  3. Ако първият знак в квадратните скоби е ^ (както е в нашия случай), значението на скобите се обръща, така че те намират всички знаци, без изброените
  4. Въпросните „изброени“ знаци са просто &, тъй като той би отделил следващия GET аргумент.
  5. Знакът + след квадратните скоби обозначава, че изброените знаци трябва да се срещат поне 1 или повече пъти.

Естествено, при IE са налице проблеми при функционалността. В скоро време ще оправим и тях.

За момента можете да видише demo на https://magadanski.com/demo/tabs-2-deep-linking/tabs.html

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

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

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