Наскоро в проект използвах 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'));
Общо взето нищо сложно. Ако някой има идеи как може да се оптимизира или направи по-добре това, ще се радвам да ги чуя.

January 16th, 2010 at 6:58 pm
И си прав и не си прав. Наскоро ми беше попаднала една статия колко “разстояние” минава мишката преди линка да бъде маркиран като “disabled” (или нещо от рода на).
Та там убави чиляк обясняваше как всеки браузър е със свое мнение и че разстоянието варира от 7 пиксела до помня ли колко.
Единствената оптимизация, която ми идва на ум е, че май правиш екстра презастраховане с “this.clearDragMarker = this.clearDragMarker.bind(this);” понеже съм почти сигурен, че this в метода ти е същия като в initialize.
January 16th, 2010 at 7:34 pm
за “this.clearDragMarker = this.clearDragMarker.bind(this);” ми трябва защото после при this.clearDragMarker.defer(); ми трябва clearDragMarker да си пази контекста на this.
В първата версия на “this.clearDragMarker.defer()” беше
(function(){ this.drag = false; }).bind(this).defer();
Но не ми харесваше всеки път да си дефинирам нова фунция, да я bind-вам и след това да я отлагам.