Постове 1 - 5 от 19 в категория Prototype

Jan 31

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

Понеделник 25.01.2010

Yehuda’s – evented programming with jQuery – това се явява като (макар и само косвено) продължение на Creating UI Elements With Low-Coupling And Conditional Event Handling, за което писах миналата седмица. Което за пореден път ми показва, че май javascript компонентите на там са се насочили.

In praise of git’s index – много добра статия за git index-ите.

Вторник 26.01.2010

The Path to Rails 3: Approaching the upgrade и rails-upgrade: Automating a portion of the Rails 3 upgrade process. Rails 3 е почти готов след малко повече от година разработка. И от където и да го погледнеш си е огромна стъпка на пред, даже няколко. В постовете се обяснява за проблеми и съвети при преминаването към Rails 3.

JRuby за Аndroid – време беше. Как ви се струва едно ruby приложение за телефон през JRuby за Android, през MacRuby за iPhone и … (не че някой ще ползва windows mobile), но IronRuby за Windows Мobile. Преди време си бях говорил с познати за такава идея и че на теория е възможно (а според тях на практика не). Но това е още една стъпка в тази посока. Въпреки че анатомиите на iPhone / Android приложенията са доста различни, все повече си мисля, че е възможно да стане нещо такова.

Answering Baranovskiy’s JavaScript quiz – Явно Вторник е бил доста мързелив ден.  Nicholas C. Zakas показва отговорите на js теста на Dmitry  Baranovskiy. Аз с радост мога да се похваля, че само за единия въпрос се подвоумих малко (на този с arguments[2] = 10).

Сряда 27.01.2010

Put that data-* attribute away, son…You might hurt someoneDan Webb е страшно уважаван от мен дивелопър. Беше част от PrototypeJs Core и беше работил по един от първите Rails плъгини за Unobtrusive JavaScript. Да не говорим колко идеи почерпих от неговата LowPro за моя CD3.Behaviors.  За нещастие, доколкото разбрах, вече работи главно с jquery.

Но да се върнем на темата, че малко се отнесох. Тук той обяснява за data-атрибутите и че колкото и да са яки, не трябва да се използват прекомерно. Поста му се явява като отговор на Yehuda’s – evented programming with jQuery.

Четвъртък 28.01.2010

Emile.js talk (video & slides)Thomas Fuchs обяснява неговия mini css animation framework – Emile.js. Който е само 51 реда (+ празните редове и Copyright-а :) ). Много полезно и абсолютно задължително за тези, които не искат цял живот само да ползват неща, които не разбират.

Why Arel – ActiveRecord 3.0 ( който е част от Rails 3 ) е изграден върху Аrel. Като наскоро  излезе и Active Record Query Interface 3.0. Но какво е Arel, ами Relational Algebra (и моята първа реакция като го чух беше – ЪЪъъ). В поста се обясняват мотивите за написването на Аrel. Важно е да се каже, че Arel е напълно отделно от Rails и може се ползва независимо.

The HTML 5 sandbox Attribute Improves iFrame Security – Една от критиките към html5 е, че не помага на този етап за сигурността на мрежата. Това е една малка стъпка напред. Жалко, че май никой не е чувал за <module> тага предложен от Douglas Crockford още в далечната вече 2006.

New ActionMailer API in Rails 3.0 – Голямото пренаписване в Rails 3 върви с пълни сили и сега ActionMailer си намери майстора. (Все пак май ActionMailer си е като недоносеното дете в Rails пакета).

Петък 29.01.2010

По-рано през седмицата Apple обявиха така дългоочаквания iPad. И общо взето никой не го харесва. Като за мен си е просто играчка. На който ако и бяха сложили просто MacOsX щяха да разбият всички, но такъв е живота. Общо взето в Петък имах много работа и нищо интересно не ми попадна. Само 2-3-4… статии за iPad. Всичките еднакви от една страна Apple хейтърите го нареждат от всякъде, Аpple феновете леко разочаровани, поради големите очаквания и само неутралните наблюдатели с положителни оценки (леко резервирани, но положителни като цяло).

Бонус

Седмицата беше много изморителна и затова нямах много време да си прегледам Google Reader-a (все още имам към 200 не прочетени статии). Затова чак днес видях серия от 3 поста от Amix, създателят на Todoist и Plurk, и първият блог, който започнах да следя.

Накрая ще сложа едно футболно клипче, на което попаднах тази седмица, за голове вкарани чрез отборна игра:

Jan 16

Наскоро в проект използвах Sortable от Script.aculo.us и имах следната html структура:

  <ul id="category_products">
    <li id="product_1"><a href="/product/1/edit"><img src="/products/1/thumb.png" /></a></li>
    <li id="product_2"><a href="/product/2/edit"><img src="/products/2/thumb.png" /></a></li>
    <li id="product_3"><a href="/product/2/edit"><img src="/products/3/thumb.png" /></a></li>
    <!-- ... и така нататък ... -->
  </ul>

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

Защо става така?

Много просто. Самото drag&drop работи така – на mousedown събитие върху li. При mouseup събитие li-то се пуска. Но в същият момент се получава и click събитие, което идва от картинката към връзката a, защото тя реално си е била натисната. И затова се получава този неприятен ефект.

Как може да се оправи това?

В Script.aculo.us има един обект Draggables, който е помощен обект грижещ се за Draggable обектите. Каквито са и нашите li-та. Той подъжа обзървъри. Като един обзървър може да има следните методи:

  • onStart – вика се, когато Draggable обект започне да се влачи
  • onDrag – вика се, докато Draggable се мести
  • onEnd – вика се след края на драгването

Вътрешно самия Sortable работи с един клас SortableObserver, който предава промените към Sortable инстанса.

И така, аз си направих един мой обзървър – DisableLinksOnDragObserver (който ползва моя Event.delegate метод).

Това, което DisableLinksOnDragObserver прави е, че когато започне да се драгва някой елемент, се слага маркер drag, и с него индикира, че в момента има влачене. След като се приключи влаченето, маркерът се прави false (тук е важно да се отбележи използването на Function#defer). Същевременно при всяко натискане на a елемент, ако drag е true, спираме събитието.

  // начин на ползване
  Draggables.addObserver(new DisableLinksOnDragObserver('category_products'));

Общо взето нищо сложно. Ако някой има идеи как може да се оптимизира или направи по-добре това, ще се радвам да ги чуя.

Nov 29

От много време не ми харесва как работя със key събития в javascript. Общо взето в по-голямата част от случаите  съм писал нещо такова:

document.observe('keyup', function(e){
	switch(e.keyCode || e.charCode){
		case Event.KEY_UP:		up();		break;
		case Event.KEY_DOWN:	down();		break;
		case Event.KEY_RIGHT:	right();	break;
		case Event.KEY_LEFT:	left();		break;
		case Event.KEY_SPACE:	fire();		break;
	}
});

Което е меко казано досадно и затова реших да направя нещо по въпроса. Реших да използвам custom събитията на Prototype.js, на които ставам все по-голям фен в последно време. И така, взех нормалните key събития – keyup / keydown / keyup и към тях добавих натиснатия бутон. Като за сега се поддържат – backspace, tab, return, esc, left, up, right, down, delete, home, end, pageup, pagedown, insert. И така горния код става така:

document.observe('keyup:up',	up);
document.observe('keyup:down',	down);
document.observe('keyup:right',	right);
document.observe('keyup:left',	left);
document.observe('keyup:space',	fire);

Кey custom събитията може да намерите тук. Ето и 2 малки демонстрации на това как работят – test1 и test2. Също така и в моя javascript playground в github ги има.

За момента само на Firefox и Safari съм ги пробвал, защото за тях ми трябваха. Но не виждам причина да не работят и под други браузъри.

Ако някои има идеи и предложения как това може да се подобри, ще се радвам да ги чуя :)

п.п. Докато работех с keypress, разбрах че имало проблем със keypress в Safari, за повече информация – http://ejohn.org/blog/keypress-in-safari-31/

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  и работи страхотно. За другите браузъри не би трябвало да има проблеми, но ако някои има проблеми с удоволствие бих помогнал.

Aug 24

В последните ми проекти почти не използвам event handler-и, който да закачам за даден елемент. Вместо това използвам event delegation и от там само слушам за събития, които се случват на долу по DOM дървото. В един по-предишен пост описвам как делегирам submit. Но за нещастие има и други проблемни събития, които не слушат спецификация зa HTML събития – а именно focus / blur.

Рядко използвам focus / blur или когато ги използвам, не ги делегирам. Но преди 3-4 седмици един познат се допита до мен. Защото имаше страница с повече от 150 input елемента, като на всеки от тях добавяше прилично количество event handler-и. Като се сложи и това, че динамично се добавяха и изтриваха доста елементи, нещата не бяха никак розови.

Като за начало всички handler-и се насочиха към document елемента, и това доста ускори нещата. Обаче focus / blur все още изискваше да се слагат отделно на всеки. Но тогава дойде на помощ хака за делегиране на focus/blur и custom събитията на Prototype.js и от тези две неща се роди това доста елегантно решение ( има го и на gist ):

(function() {
    function focusInHandler(e){
        Еvent.element(e).fire("focus:in");
    }
    function focusOutHandler(e){
        Еvent.element(e).fire("focus:out");
    }

    if (document.addEventListener){
        document.addEventListener("focus", focusInHandler, true);
        document.addEventListener("blur", focusOutHandler, true);
    } else {
        document.observe("focusin", focusInHandler);
        document.observe("focusout", focusOutHandler);
    }
})();

Интересното е, че от 1-2 седмици ми се налага да използвам доста често новите focus:in / focus:out , което от една страна доста изчиства и ускорява кода ми. Но и от друга страна прави доста по userfriendly самите компоненти които правя, така че всички са доволни. Като даже ако имам време мисля да пренапиша и кода за делегиране на  submit. :)