Warehouse вече е open source 50 важни момента от историята на Интернет
Oct 03

Наскоро направих, нещо което от доста време планирам - да събера публикувам част от JavaScript нещата които ползвам, като opensource. Така че ето ги ControlDepo 3 Widgets:

http://github.com/RStankov/controldepo-3-widgets/tree/master

Тук смятам да събера, основните JavaScript неща, които имам като custom form полета, ефекти, prototype допълнения и други подобни. И също така ще публикувам във блога и статии с които да описвам как се работи с дадения компонент (така най-сетне ще имам документация). И така първия и може би любимия ми :

CD3.Behaviors

CD3.Behavoirs e вдъхновен от CSS event:Selectors на Justin Palmer и LowPro на Dan Webb ( както и Behavior на  Ben Nolan, който вече го няма). Общо взето от около една и половина го ползвам и развивам и мисля че доста полезен. Работата на CD3.Behavoirs, условно се разделя на 4ри части:

нормален селектор | event-селектори | event-delegation | инстанциране на класове

Нормалния селектор просто селектира определения css селектори и вика върху всеки елемент подадената функция, като съответния елемент  се предава като първи аргумент и също така функцията се bind-ва към него т.е. като в подадената функция this е този елемент за който тя се извиква. Ето един малък пример:


CD3.Behaviors({
	'#container1': function(){
		this.insert('<strong>текст</strong>');
	},
	'#container1 a': function(a){
		a.observe('click', function(){ alert('#container1 a clicked'); })
	},
	'#container1 span': function(){
		alert('This will not excecute');
	}
});

Горния пример  има 3 селектора:

  1. #container1 - избира елемент с id = container1 и после в него добавя <strong>текст</strong> вътре в него
  2. #container1 a - избира всеки а елемент от container1 и добавя click event към него ( тук а се подава като аргумент, но може да се напише и this.observe )
  3. #container1 span - просто показва че ако няма елемент отговарящ на селектора, функцията не се вика

Event-селекторa разглежда селектора на 2 части {селектор част}:{event част}. Като намира всички елементи от дадения селектор и им добавя event listener-и (чрез Event.observe). Действията са много подобни на тези на CSS event:Selectors на Justin Palmer, да не кажа същите, с някои подобрения.


CD3.Behaviors({
	'#container2 a:click': function(){
		this.toggleClassName('clicked')
	},
	'#container2 a.mouse': {
		mouseover: function(){
			this.innerHTML = 'mouseover';
		},
		mouseout: function(){
			this.innerHTML = 'mouseout';
		}
	}
});

Какво става тук:

  1. #container2 a:click - избира всички а-та от #container2 и им добавя при click event да си сменят className -а
  2. #container2 a.mouse- е малко по-интересно. То селектира всички а-та с клас “mouse” в #container2 и започва да наблюдава (observe) две действия:
    • mouseover - просто добавя текст “mouseover” във а-тa
    • mouseout -просто добавя текст “mouseout” във а-то

Горния пример със нормален селектор би изглеждал така:


CD3.Behaviors({
	'#container2 a': function(a){
		a.observe('click', function(){
			this.toggleClassName('clicked');
		});
	},
	'#container2 a.mouse': function(a){
		a.observe('mouseover', function(){
			this.innerHTML = 'mouseover';
		});
		a.observe('mouseout', function(){
			this.innerHTML = 'mouseout';
		});
	}
});

Но просто има твърде много излишен код тук, а и в като се пише JavaScript - the size matters!

Event-delegation - тук става малко по “сложно” … за обяснение.  По принцип Event-delegation-a е доста лесен, и особено при по-динамични и натоварени javascript приложения си е задължителна практика. И понеже аз доста често го използвах реших да го вкарам във CD3.Behaviors като ползвам нещо подобно на Event.delegate от LowPro. Като основната ми цел беше да го “скрия” така да не се натрапва и според мен стана доста добре:


CD3.Behaviors({
	'#container3:click': {
		'span': function(){
			alert('span was clicked, span innerHTML is "' + this.innerHTML + '"');
		},
		'a': function(){
			alert('link was clicked, span innerHTML is "' + this.innerHTML + '"');
		},
		'div': function(){
			alert('nothing was clicked');
		}
	},
});

Така, какво става тук ? Ами, със нормален event-selector избираме #container3 и му добавяме click event. Само, че когато се натисне #container3 започва да се проверяват подадените селектори - span, a, div, в случая, и когато натиснатия елемент отговаря на някоя селектор се вика съответната функция, който е била зададена към селектора ( като даже и scope-a на функцията се сетва да е съответния елемент, така че ако е натиснат ’span’ елемент this ще е този елемент)

По начина по който съм направил event-delegation-a може да се пишат и такива неща:


CD3.Behaviors({
	'#container3': {
		mouseover: {
			'span': function(){
				this.addClassName('clicked');
			},
			'a': function(){
				this.addClassName('clicked');
			}
		},
		mouseout: {
			'span': function(){
				this.removeClassName('clicked');
			},
			'a': function(){
				this.removeClassName('clicked');
			}
		}
	}
});

Което е все едно да имаме 2 event-селектора - #container3:mouseover и #container3:mouseout, но горния код е доста по-бърз защото селектираме само ведъж #container3 :) да не говорим че е и доста по ясен.

Инстанциране на класове. Едно от най-яките неща в LowPro бяха Behavoirs класовете, обаче така и не ги използвах никъде, а и предпочитам да ползвам нормални prototype класове. Затова направих най-нормалното, което ми се виждаше, да направя всеки нормален prototype клас (без да променям нищо в prototype) да работи със CD3.Behaviors:


var TestWidget = Class.create({
	initialize: function(element, options){
		this.value = options || 0
		element.observe('click', this.click.bind(this));
	},
	click: function(){
		alert('widget with ' + this.value + ' was clicked');
	}
});
CD3.Behaviors({
	'#container4 a.first': TestWidget,
	'#container4 a.second': [TestWidget, 5]
});

Това, може да се напише и по този начин :


CD3.Behaviors({
	'#container4 a.first': function(){
		new TestWidget(this);
	},
	'#container4 a.second': function(){
		new TestWidget(this, 5);
	}
});

Единственото, лошо тук е че [TestWidget, 5] не може да приема (за сега) повече от един параметър, който играе ролята на options. А и повечето Widget класове, които ползвам са само със element и options аргументи.

Това горе-долу са основните части от CD3.Behaviors. В идните сигурно ще напиша как работи CD3.Bahaviors.when и как аз обикновенно използвам CD3.Bahaviors.

2 Responses to “ControlDepo 3 Widgets - Behaviors Част I”

  1. shterev Says:

    статийката е наистина мн добре обяснена, а и е мн полезна. :)

  2. Radoslav Stankov Says:

    :)
    Забравих да спомена, че тук: http://github.com/RStankov/controldepo-3-widgets/tree/master/tests/functional/behavior.html има пример за това как се използва.

Leave a Reply

  • Enter this code