Javascript-форум (https://javascript.ru/forum/)
-   Общие вопросы Javascript (https://javascript.ru/forum/misc/)
-   -   Делегирование события (https://javascript.ru/forum/misc/44119-delegirovanie-sobytiya.html)

ruslan_mart 07.01.2014 17:53

Делегирование события
 
Приветсвую всех, уважаемые пользователи форума! :)

Написал такой скрипт для делегирования событий:

(function() {
	var d = document.documentElement,
		m = d.matchesSelector || d.webkitMatchesSelector || d.oMatchesSelector || d.mozMatchesSelector || d.msMatchesSelector;
	
	Element.prototype.addEventDelegate = function(type, selector, fn) {
		this.addEventListener(type, function(e) {
			e = e && e.target || window.event.srcElement;
			if(m.call(e, selector)) {
				fn.call(e);
			}
		}, false);
	}
})();


window.onload = function() {
	document.body.addEventDelegate('click', 'p', function() {
		alert('Вы кликнули по тэгу "p"!');
	});
}


Всё работает, но... когда кликаешь по более вложенному элементу, то событие для родительского элемента не срабатывает. Нужно, чтобы всё работало как в jQuery. Кто-нибудь знает, как более грамотно так сделать?
Спасибо.

BallsShaped 07.01.2014 18:35

Цитата:

Сообщение от Ruslan_xDD
когда кликаешь по более вложенному элементу, то событие для родительского элемента не срабатывает.

Странный вывод. Не важно, насколько вложен элемент. Проверяешь на соотв-ие селектору ты только элемент, по которому кликнули. Очевидно, нужно проверять все элементы от e.target до this.

Vlasenko Fedor 07.01.2014 22:29

while (e != this) {
            //работаем
            e = e.parentNode;
}

BallsShaped,
правильно ответил

danik.js 07.01.2014 22:46

К слову в черновик метод попал под именем matches(), так что исправь. Пока что все браузеры используют старые префикснутые matchesSelector()

Вобще странно. Тогда и document.querySelector() надо было переименовать в document.query()

ruslan_mart 09.01.2014 13:26

(function() {
	var d = document.documentElement,
		m = d.matchesSelector || d.webkitMatchesSelector || d.oMatchesSelector || d.mozMatchesSelector || d.msMatchesSelector || d.matches;
	
	Element.prototype.addEventDelegate = function(type, selector, fn) {
		this.addEventListener(type, function(e) {
			e = e && e.target || window.event.srcElement;
			if(m.call(e, selector + ', ' + selector + ' *')) {
				fn.call(e);
			}
		}, false);
	}
})();


Вот так пока сделал, но думаю, что сделаю по другому как время будет. :)

ruslan_mart 09.01.2014 13:28

Poznakomlus, спасибо, суть уловил. :)

danik.js 09.01.2014 14:58

Цитата:

Сообщение от Ruslan_xDD
d.matchesSelector

Нет такого свойства. И не будет никогда. Читай мой коммент.

ruslan_mart 09.01.2014 15:13

danik.js, понял. :)

Доделал. :)
(function() {
	var d = document.documentElement,
		m = d.matches || d.webkitMatchesSelector || d.oMatchesSelector || d.mozMatchesSelector || d.msMatchesSelector;
	
	Element.prototype.addEventDelegate = function(type, selector, fn) {
		this.addEventListener(type, function(e) {
			var t = e && e.target || window.event.srcElement;
			while(t != this) {
				if(m.call(t, selector)) {
					fn.call(t, e);
					break;
				}
				t = t.parentNode;
			}
		}, false);
	}
})();

danik.js 09.01.2014 15:31

Ruslan_xDD, хотя фиг его знает)) Мож до релиза еще раз пять сменят название ))

ruslan_mart 09.01.2014 15:38

danik.js, только вот хрен знает как будет работать, если браузер вообще не поддерживает matches. :) Может как-нибудь через querySelector запилить?

danik.js 09.01.2014 16:18

Посмотри как это сделано в jQuery

danik.js 10.01.2014 01:44

Цитата:

Сообщение от Ruslan_xDD
this.addEventListener

Но при этом
Цитата:

Сообщение от Ruslan_xDD
e && e.target || window.event.srcElement

Как это понимать? Если ты не поддерживаешь IE8 (ибо в нем нет addEventListener), то нет смысла в window.event.

W3C модель: addEventListener + event.target
old IE модель: attachEvent + window.event.srcElement

ruslan_mart 10.01.2014 05:21

danik.js, это я сюда тестовый пример кинул, я сам там для себя пишу мини-библиотеку, там всё это предусмотрено, а сюда просто написал понятный код, так как код из библы написан в стиле ниндзя и может быть не понятен. :D

.......
	_e = 'addEventListener' in w ? 'addEventListener' : 'attachEvent';
	_eD = dE.matches || dE.matchesSelector || dE.webkitMatchesSelector || dE.oMatchesSelector || dE.mozMatchesSelector || dE.msMatchesSelector;
	.......
	.......
	.......
	e: function(a, b) { //event
		this[_e](a, b, false);
		return this;
	},
	eD: function(a, b, c) { //event delegate
		this.e(a, function(e) {
			var t = e && e.target || w.event.srcElement;
			while(t != this) {
				if(_eD.call(t, b)) {
					c.call(t, e);
					break;
				}
				t = t.parentNode;
			}
		});
		return this;
	},
	.......

danik.js 10.01.2014 06:01

Ну мог бы хотя бы необфусцированный код показать. Или ты так и пишешь ? :D

Цитата:

Сообщение от Ruslan_xDD
while(t != this) {

В IE8 this будет указывать на window, а не на event.currentTarget

ruslan_mart 10.01.2014 12:48

danik.js, я сразу так и пишу, a,b,c и т.д., так как для себя пишу, мне лично всё понятно будет. :D

Цитата:

Сообщение от danik.js
В IE8 this будет указывать на window, а не на event.currentTarget

Сколько же проблем с этими ослами. :(

danik.js 10.01.2014 14:07

Цитата:

Сообщение от Ruslan_xDD
danik.js, я сразу так и пишу, a,b,c и т.д., так как для себя пишу, мне лично всё понятно будет.

Не обольщайся. Через месяц ты спросишь: блядь, я че, бухой был, когда писал это? Код очень быстро забывается, и то, что ты отлично понимаешь сегодня, завтра будет выглядеть как какая-то шифровка.

melky 10.01.2014 14:15

Цитата:

Сообщение от danik.js (Сообщение 291425)
то, что ты отлично понимаешь сегодня, завтра будет выглядеть как какая-то шифровка.

вот в этом и отличие хорошего кода от просто кода :)

ruslan_mart 10.01.2014 14:16

danik.js, да, бывает такое, когда смотрю старые исходники, которые писал около месяца назад и вообще ничего не понимаю. Но сейчас я стараюсь оставлять комментарии, чтобы не запутаться. :) Только вот когда уже буду ставить скрипт на сайт, то все комментики потру и всё сделаю в одну строку. :D Я конечно понимаю, что код мой нафиг никому не нужен будет, но сделаю так из принципа. :D

danik.js 10.01.2014 15:17

Цитата:

Сообщение от Ruslan_xDD
я стараюсь оставлять комментарии, чтобы не запутаться

Есть мнение что комментарии - признак плохого кода. Хороший код должен быть понятен и без комментов. Как минимум нужно давать понятные имена переменным.

ruslan_mart 10.01.2014 15:24

danik.js, ну это то понятно, я оставляю комментарии около самих ф-ций, чтобы не вспоминать, что она означатает. Например: cA - это classAdd, eD - eventDelegate и т.д.

Цитата:

Сообщение от danik.js
Есть мнение что комментарии - признак плохого кода.

Сам комментарии именно к коду никогда не использую, а если использую, то только когда заказщик попросит описать ему код. :)

Vlasenko Fedor 10.01.2014 16:06

Цитата:

Сообщение от danik.js (Сообщение 291438)
Есть мнение что комментарии - признак плохого кода

Неправильное мнение. Так как система комментариев иногда используется для подсветки параметров в редакторах
А вот за a, b, c сразу на хлеб да воду сажать. :)

ruslan_mart 10.01.2014 16:18

Цитата:

Сообщение от Poznakomlus
А вот за a, b, c сразу на хлеб да воду сажать.

Почему же? Если пишешь для кого-то, то - да, а если для себя - то ничего в этом плохого нет. Мне просто так удобнее писать, не нужно заморачиваться со всякими именами переменных и т.д., а главное - код получается меньше. :)

melky 10.01.2014 16:21

Цитата:

Сообщение от Ruslan_xDD (Сообщение 291442)
danik.js, ну это то понятно, я оставляю комментарии около самих ф-ций, чтобы не вспоминать, что она означатает. Например: cA - это classAdd, eD - eventDelegate и т.д.

cA

ужас

зачем так мало?

Цитата:

Сообщение от Ruslan_xDD (Сообщение 291442)
Сам комментарии именно к коду никогда не использую, а если использую, то только когда заказщик попросит описать ему код. :)

ты для программистов код пишешь?

ruslan_mart 10.01.2014 16:24

Цитата:

Сообщение от melky
зачем так мало?

Ну что поделать, люблю ниндзить, когда пишу для себя. :D :D :D

Цитата:

Сообщение от melky
ты для программистов код пишешь?

Программисты пусть сами себе код пишут, на то они и программисты. :D

Vlasenko Fedor 10.01.2014 16:45

Цитата:

Сообщение от Ruslan_xDD
а главное - код получается меньше

для этого существуют компиляторы, упаковщики, сборщики и т. Д
http://closure-compiler.appspot.com/home
http://compressorrater.thruhere.net/
.................................................

danik.js 10.01.2014 23:47

Цитата:

Сообщение от Poznakomlus
Неправильное мнение. Так как система комментариев иногда используется для подсветки параметров

Ну не об этом же речь. Без фанатизма. Речь как раз о таких вот примерах, как у Ruslan_xDD, когда лучше дать понятное имя функции и ее параметрам (а также локальным переменным), нежели писать поясняющие комментарии. Еще в JSDOC дается описание параметров, хотя чаще всего оно просто дублирует инфу, уже вложенную в имя самого параметра:
/**
     * Sets a new editsession to use. This method also emits the `'changeSession'` event.
     * @param {EditSession} session The new session to use
     *
     *
     **/
    this.setSession = function(session) {

ruslan_mart 11.01.2014 07:23

Ладно, уговорили. :)

delegate: function(type, selector, callback) {
	this.event(type, function(event) {
		var target = event && event.target || w.event.srcElement;
		while(target != this) {
			if(matches.call(target, selector)) {
				callback.call(target, event);
				break;
			}
			target = target.parentNode;
		}
	});
	return this;
},
event: function(type, callback) {
	var self = this;
	if('addEventListener' in self) self.addEventListener(type, callback, false);
	else self.attachEvent('on' + type, function(event) {
		callback.call(self, event);
	});
	return this;
},

nerv_ 11.01.2014 14:26

Цитата:

Сообщение от Poznakomlus
А вот за a, b, c сразу на хлеб да воду сажать

от контекста зависит

Цитата:

Сообщение от Ruslan_xDD
Если пишешь для кого-то, то - да, а если для себя - то ничего в этом плохого нет

первый признак говнокодера

Цитата:

Сообщение от Ruslan_xDD
Мне просто так удобнее писать, не нужно заморачиваться со всякими именами переменных

если ты не можешь придумать имя переменной - это говорит об уровне кодера

ruslan_mart 11.01.2014 14:37

Цитата:

Сообщение от nerv_
если ты не можешь придумать имя переменной - это говорит об уровне кодера

Могу, просто я люблю ставить правильные имена, не всякие var data, ololo, sobitie и т.д., а именно правильные: callback, formRemove, setHelper и т.д. Но иногда бывает влом придумывать имя.

Цитата:

Сообщение от nerv_
первый признак говнокодера

Не считаю себя говнокодером. ;)

var d = document,
	h = d.documentElement,
	i, mk,
	w = window;

(function() {	
	mk = function(selector) {
		if('mk' in this) return mk.call(typeof selector == 'object' ? selector : d.querySelector(selector));
		for(i in _mk) this[i] = _mk[i];
		return this;
	};
	var XMLRequest = XMLHttpRequest || w.ActiveXObject && function() {
			return new ActiveXObject('Msxml2.XMLHTTP');
		},
		reqTimeout,
		classList = 'classList' in h,
		matches = h.matches || h.matchesSelector || h.webkitMatchesSelector || h.oMatchesSelector || h.mozMatchesSelector || h.msMatchesSelector || function(selector) {
     		var elems = this.parentNode.querySelectorAll(selector);
			for(i=0; i<elems.length; i++) if(elems[i] === this) return true;
		},
		_mk = {
			append: function() {
				var childs = arguments;
				for(i=0; i<childs.length; i++) this.appendChild(childs[i]);
				return this;
			},
			classAdd: function(name) {
				if(classList) this.classList.add(name);
				else this.className += this.classHas(name) ? '' : (this.className ? ' ' + name : name);
				return this;
			},
			classHas: function(name) {
				return classList ? this.classList.contains(name) : !!~(' ' + this.className + ' ').indexOf(' ' + name + ' ');
			},
			classRemove: function(name) {
				if(classList) this.classList.remove(name);
				else if(this.classHas(name)) this.className = this.className.replace(new RegExp(' ?' + name + ' ?'), '');
				return this;
			},
			classToggle: function(name) {
				if(classList) this.classList.toggle(name);
				else this.classHas(name) ? this.classRemove(name) : this.classAdd(name);
				return this;
			},
			delegate: function(type, selector, callback) {
				this.event(type, function(event) {
					var target = event && event.target || w.event.srcElement;
					while(target != this) {
						if(matches.call(target, selector)) {
							callback.call(target, event);
							break;
						}
						target = target.parentNode;
					}
				});
				return this;
			},
			effect: function(name, remove) {
				var self = this;
				self.classToggle('mk-' + name);
				if(remove) setTimeout(function() {
					self.remove();
				}, 500);
				return self;
			},
			event: function(type, callback) {
				var self = this;
				if('addEventListener' in self) self.addEventListener(type, callback, false);
				else self.attachEvent('on' + type, function(event) {
					callback.call(self, event);
				});
				return this;
			},
			props: function(props) {
				for(i in props) this[i] = props[i];
				return this;
			},
			query: function(selector) {
				return this.querySelector(selector);
			},
			remove: function() {
				this.parentNode.removeChild(this);
			},
		};
	
	mk.create = function(tagName, props) {
		var elem = d.createElement(tagName);
		mk(elem).props(props);
		return elem;
	};
	mk.message = function(icon, title, message, clicked) {
		var elem = mk.create('div', {className: 'mk-msg mk-show'}),
			img = mk.create('img', {align: 'left', alt: '', src: icon}),
			div = mk(mk.create('div')).append(mk.create('b', {textContent: title}), mk.create('span', {textContent: message})),
			params = arguments.callee;
		elem = mk(elem).append(img, div);
		mk('#mk-msgs').insertBefore(elem, params.len === 0 ? null : params.tmp);
		params.len = params.len + 1 || 1;
		params.tmp = elem;
		var timeout = setTimeout(elem.onclick = function() {
			elem.effect('show', true);
			params.len--;
			clearTimeout(timeout);
			if(clicked) clicked();
		}, 4E3);
		elem.clientWidth;
		elem.effect('show');
	};
	mk.request = function(url, params, callback, error) {
		var keys = [],
			req = new XMLRequest(),
			self = this;
		req.open('POST', url, true);
		req.onreadystatechange = function() {
			if(req.readyState != 4) return;
			clearTimeout(reqTimeout);
			if(req.status == 200) callback.call(self, req.responseText);
			else if(error) error(req.statusText);
		};
		req.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
		for(i in params) keys.push(i + '=' + params[i]);
		req.send(keys.join('&') || null);
		reqTimeout = setTimeout(function() {
			req.abort();
			if(error) error();
		}, 5E3);
	};
})();


ТЕСТ! :)

На class* не знаю, ставить регулярку или оставить такой костыль?

И ещё вопрос: насколько кроссбраузерен будет XMLHttp запрос (mk.request)? Ничего не упустил?

melky 11.01.2014 16:05

Цитата:

Сообщение от danik.js (Сообщение 291438)
Есть мнение что комментарии - признак плохого кода. Хороший код должен быть понятен и без комментов. Как минимум нужно давать понятные имена переменным.

как workaround'ы делать без комментариев?
как хаки пояснять без комментариев?

это хороший код ... плохие браузеры\стандарты и плохое API :)


Часовой пояс GMT +3, время: 23:50.