Sep 01

Преди малко по-малко от година в един пост озаглавен “Малко JavaScript refactoring“, бях описал как работи прост таб панел. Части от него използвам и до сега. Но е време за промяна.

Това което не ми допада в тогавашната версия е, че когато в страницата се добавя нов таб панел трябва да се вика отново TabPanel. А ако трябва да се трие нещата стават доста сложни. Също така и добавянето и/или изтриването на табчета става доста трудно. Затова реших да напиша нещо малко по-бързо и по-лесно.

В последните ми няколко проекта по които работя, ще поддържат само Firefox 3.5 и Safari 4, и  нагоре. Браузъри като IE (6/7/8) и даже Opera, въобще не влизат в картинката. Това ми дава възможност да се насладя на работата със html5 / css3. Затова структурата на един таб панел изглежда така:

<div class="tab_panel">
	<nav>
		<ul>
			<li class="tab selected">tab 1</li>
			<li class="tab">tab 2</li>
			<li class="tab">tab 3</li>
		</ul>
	</nav>
	<section></section>
	<section style="display: none;"></section>
	<section style="display: none;"></section>
</div>

А javascript, който задвижва нещата е:

function changeTab(){
	this.up('.tab_panel').select('section').invoke('hide')[this.previousSiblings('.tab').length].show();
	this.up().select('.selected').invoke('removeClassName', 'selected');
	this.addClassName('selected');
}
document.delegate('.tab_panel .tab', 'click', changeTab);

Как работи кода?

Под ‘this’ се има предвид избрания таб. На ред първи, се “качваме” до елемента, съдържащ таб панела и крием всички ‘section’ елементи. И след това показваме само избрания от нас елемент. Като this.previousSiblings(‘.tab’).length, ни връща колко таб бутна има преди избрания таб, което е и индекса на избрания елемент.
На втори ред.  Просто избирам всички елементи който имат клас “selected” (което отбелязва кое е избрания таб) и махам този клас от тях. Тук само искам да отбележа, че бих могъл да ползвам Element.down и после само Element.removeClassName на един елемент. Но съм предпочел да взема малко по “jquery” решение на въпроса.
И третия последен ред е просто слага ‘selected’ клас на избрания таб, маркирайки го като активен.

И след това моят Event.delegate, слуша за натискания на таб бутна и след това просто стартира changeTab.

На фокус

До преди няколко дни използвах този код при click събитие, но напоследък след по-голямото популяризиране на focus:in/focus:out и атрибута tabindex реших и да пробвам нещо малко “по-нестандартно” и така да направя таб панела достъпен и чрез “tab” бутона. :)

<div class="tab_panel">
	<nav>
		<ul>
			<li class="tab selected" tabindex="1">tab 1</li>
			<li class="tab" tabindex="2">tab 2</li>
			<li class="tab" tabindex="3">tab 3</li>
		</ul>
	</nav>
	<section></section>
	<section style="display: none;"></section>
	<section style="display: none;"></section>
</div>
function changeTab(){
	this.up('.tab_panel').select('section').invoke('hide')[this.previousSiblings('.tab').length].show();
	this.up().select('.selected').invoke('removeClassName', 'selected');
	this.addClassName('selected');
}
document.delegate('.tab_panel .tab', 'focus:in', changeTab);

Най-хубавото тук е че ‘click’ не ни трябва защото при натискане на елемент-а focus събитието се изстрелва така или иначе. Също така ако не искате се чудите за tabindex-а в какъв ред да е, просто може да се сложи навсякъде да е 1.

Примерен таб панел съм качил тук. Той изисква 2 неща (освен Prototype.js) – Event.delegate и focus:in.

За момента съм го тествал само в Firefox 3.5 / Safari4  и работи страхотно. За другите браузъри не би трябвало да има проблеми, но ако някои има проблеми с удоволствие бих помогнал.

7 коментара за "Бърз таб панел в четири, използващ Prototype Js"

  1. Иван каза:

    Специално за таб панелите, мисля, че трябва да са достъпни (accessible) т.е. да са с линк в LI елемента, който да води към анкор в някой от следходните DIV / SECTION. Всъщност, щом е HTML 5, може и LI-то да има href.

    Така скрипта се променя, че делегираш клик / фокус върху линковете и намираш елемента по търсения анкор. Друг вариант е да делегираш (слушаш, ако делегирането не е възможно) # събитие т.е. когато фокуса дойде върху даден DIV / SECTION да го покажеш и да скриеш предния (референция към който се пази в TAB обекта на инстанцията).

    Разбираемо ли го написах?

    Когато се прибера, мога да ти пусна снипет на първия вариант с линковете. Работи безотказно.

    – Иван

  2. Иван каза:

    Също, ако някои неща ги допуснеш — примерно, че UL е първия child (не Node) на таб панела — скрипта става по-лек.

  3. Radoslav Stankov каза:

    Да може доста добре може да стане, ще съм ти благодарен ако ми пуснеш връзка към снипет с твоя вариант :)
    Ако имаш github акаунт може направо да fork-неш този gist – http://gist.github.com/179320

  4. Иван каза:

    Това е бързия вариант — http://gist.github.com/179708

    Ако се приеме, че се работи с ХТМЛ 5, то може да се работи с querySelector, което опростява селекторите. Ще го променя като се прибера.

  5. Иван каза:

    Коментари за това — http://gist.github.com/179719 — slice(0), а не 1, както го бях написал е нужен за да премахнеш първия сиблинг от масива т.е. навигацияат. Иначе рискуваш да скриеш всички елементи ;)

  6. Radoslav Stankov каза:

    Ами доста добре става :) само дефинирането не многото event handler-и не ми допада много, а и focus:in още може да ползва :)

    Това за сега е оптималния вариант и все още е четири реда: http://gist.github.com/180007 ( Утре сигурно ще го вмъкна като update на поста ), благодаря за помощта :)

  7. Иван каза:

    Това навързване на методите го направих заради майтапа — честно казано ми се струва по-правилно първо да се скрият другите елементи и след това да се покаже текущия, а не обратно. Допълнително, предпочитам да си разписвам методите на нови редове — ей така да не се губя.

    Но от друга страна това действие отнема няколко тика и е на практика неусетно. И не на последно място — така е много по-кратко или поне създава такова усещане.

    П.П.
    Принципно цъкам основно ЦСС, а ДжаваСкрипта ми е хоби, така че като пиша снипети ги проверявай за грешка.

Какво мислите по въпроса