Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > JavaScript: Общие вопросы > Добавление обработчика события


Автор: 12345c 21.12.2006, 22:14
Известно, что есть функция добавления обработчика к фиксированнному событию, например, к onload. Смысл её такой, что если в обработчике обнаруживается function, то он переопределяется как новая функция, составленная из последовательности двух. С такой функцией добавления нет проблем, она работает. Но понадобился добавитель к переменному обработчику. Проблемы началисть с того, что передать объект-обработчик как параметр не получилось - передаётся почему-то не объект, а его копия (потому что внутри функции присваивание работает, а по окончании значение объекта-обработчика остаётся старым. Вот пример неработающего кода, но не будем на нём долго останавливаться:
Код
onresize=function(){abcde=12345;
fghij=67890;}
DebMonScrl=function(){a=12345;
b=67890;}
addEvent=function(eve,f){var f0;
  eve=typeof(eve)=='function'?(f0=eve,new Function('ev','('+f0.toString()+')(ev);('+f.toString()+')(ev)')):f;
  alert(eve)
}
addEvent(onresize,DebMonScrl);
alert(onresize)
Видим, что прекрасно присваивается, а "потом - опять за старое". Чем его лечить? Делать нечего, методы должны быть суровые. Берём имя обработчика и грубым выполнением выжигаем в нём новое значение:
Код
addEvent=function(evS,f){var f0;
  eval(evS+'='+(typeof(f0=eval(evS))=='function'?new Function('ev','('+f0.toString()+')(ev);('+f.toString()+')(ev)'):f).toString()+';');
return f;}
//...далее можем:
addEvent('onresize',addEvent('onscroll',function(){alert(12345);}));
Нормально, работает, но тут незадача случается с Оперой: до версии 9.00 она отказывается выполнять в eval() присваивание функции или вообще употребление функции - диагностика невнятная. Более-менее чётко говорит об этом она, начиная с 8.0, что у них, мол, это не принято. Как же быть? Ради гибкости скрипта ударились во все тяжкие, применили eval, получили желаемое (переменный обработчик), а в старых Операх пользователям придётся прозябать или разработчикам работать по-старинке?

Кто предложит решение? (до версии Опера 8.53)) Чтобы присвоить переменному обработчику новую функцию, но не применять function в eval? И вообще, почему не присвоилась функция в первом случае и можно ли сделать красИвее?

Дополнение: Получилось решение, оставляю задачку для тренировки умов, она несложная, из серии "не в лоб, так по лбу" smile.
...А Опера 9.0 с отрисовкой заметно помедленнела. Интересно, исправят ли, или это дань продвинутости?

Автор: Zeroglif 21.12.2006, 23:56
Цитата(12345c @  21.12.2006,  22:14 Найти цитируемый пост)
Проблемы началисть с того, что передать объект-обработчик как параметр не получилось - передаётся почему-то не объект, а его копия (потому что внутри функции присваивание работает, а по окончании значение объекта-обработчика остаётся старым.

В javascript всё передаётся по значению! В твоём случае ты передаёшь ссылки (это и есть значение для объектных типов) на анонимные функции. Конструктор строит новую функцию, обратившись по ссылкам к "переменным" (и f0). При этом свойство window с именем onresize как хранило ссылку на свою анонимную функцию, так и хранит, чтобы что-то изменить надо сделать что-то вроде onresize = eve, тогда ты "забьёшь" предыдущую ссылку новой...

Автор: 12345c 22.12.2006, 00:17
Получается, что eval+имя - единственно верное направление.

Автор: Nicholas_S 22.12.2006, 00:18
12345c, постановка задачи ясна. Единственное, у меня твой второй пример не работает ни в 8, ни в 9ой версиях Оперы.
Есть одно решение, насколько оно тебя удовлетворит не знаю, но работает и в 8ой, и в 9ой Опере. За оптимальность не отвечаю, предлагаю только ход мыслей:
Код

...

Function.prototype.addFunc = function(selfName, func)
{
    eval(selfName + ' = function(){var f0='+this.toString() + '; var f1=' + func.toString() +'; f0(); f1();}');
}
onresize.addFunc('onresize', DebMonScrl);

Что неудобно, так это указание самого имени функции, к которой добавляем новую. Может быть сделаешь удобнее.

Автор: Zeroglif 22.12.2006, 00:31
Цитата(12345c @  22.12.2006,  00:17 Найти цитируемый пост)
Получается, что eval+имя - единственно верное направление.

Зачем eval, если известно событие и его имя передаётся аргументом?

Автор: 12345c 22.12.2006, 02:34
Да, можно addEventListener. Только покрасивее со сцеплением функций не придумывается.

Nicholas_S, я примерно так решил (функция в строке), только без прототипа, потому что получается тавтология с именами. (И важно, чтобы параметр у функции был.) Или придётся брать String.prototype.addFunc , чтобы не повторяться.

Автор: Sardar 22.12.2006, 04:26
Цитата(12345c @  21.12.2006,  21:14 Найти цитируемый пост)
Но понадобился добавитель к переменному обработчику. Проблемы началисть с того, что передать объект-обработчик как параметр не получилось - передаётся почему-то не объект, а его копия (потому что внутри функции присваивание работает, а по окончании значение объекта-обработчика остаётся старым.

Что то жуткое читаю и не врубаюсь в смысл слов... правильно ли я понял, что ты думал аргументы передаются по указателю (изменяемые)? Тогда вообще не понятно, в каждом учебнике же лежит, что всё передаётся (даже примитивы, помним что они immutable) по ссылке, присваивание которой просто изменяет ссылку, а не обьект, на который она ссылалась. Так и в Java и в C#... языки поддерживающие указатели по пальцам можно пересчитать (PHP, C/C++, ...? )

Термин указатель == ссылка, '=' оператор для которой определён как изменяющий значение обьекта, на который ссылка указывает (разыменование). Сишные указатели в сторону smile 
В современных языках только примитивы ещё могут по ссылке изменяться, и то не всегда. Для остальных типов меняется именно значение самой ссылки, а не обьект на который она указывает


Кстати вызов toString() на closure выложит его исходник, естественно контекст заменяется на тот, что в eval'е (от window/global). Что ты вообще хотел этим сделать не пойму...

Интересно кому в наше время ещё нужны традиционные события... А если не извращаться, то будет так:
Код
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xml:lang="en" lang="en" xmlns="http://www.w3.org/1999/xhtml">
<head>
<script type="text/javascript">

function createDispatcher() {
    var handlers = [];
    //callable by event
    var eventDispatcher = function(ev) {
        for(var i=0; i < handlers.length; i++) handlers[i](ev);
    }
    
    //add new handler
    eventDispatcher.addHandler = function(handler) {
        handlers.push(handler);
    }
    return eventDispatcher;
}

function addEvent(obj, event_type, new_handler) {
    if(!obj[event_type] || !obj[event_type].addHandler) { //install new dispatcher
        var disp = createDispatcher();
        if(obj[event_type]) disp.addHandler(obj[event_type]);
        obj[event_type] = disp;
    }
    obj[event_type].addHandler(new_handler);
}


window.onresize = function() {
    document.getElementById("dbg").innerHTML = "Original handler: " + document.documentElement.offsetWidth + ", " + document.documentElement.offsetHeight;
}

var another = function() {
    document.getElementById("dbg1").innerHTML = "Added handler: " + document.documentElement.offsetWidth + ", " + document.documentElement.offsetHeight;
}

var third = function() {
    document.getElementById("dbg2").innerHTML = "Added handler: " + arguments.callee.test++;
}
third.test = 0;

addEvent(window, 'onresize', another);
addEvent(window, 'onresize', third);
</script>
</head>
<body>
    <div id="dbg"></div>
    <div id="dbg1"></div>
    <div id="dbg2"></div>
</body>
</html>



P.S. код отвратителен по читабельности, неужели нельзя давать нормальные имена и разбивать по строкам? попытка "закодировать" тривиальный код не одно и тоже с попыткой написать код гениальный, но мало кому понятный smile

Добавлено @ 04:33 
Цитата(Zeroglif @  21.12.2006,  22:56 Найти цитируемый пост)
В javascript всё передаётся по значению! 

Zeroglif, больше такого никому не говори smile
Если желаешь поспорить, то в начале в личку, флейм тут не нужен.

Цитата(12345c @  21.12.2006,  23:17 Найти цитируемый пост)
Получается, что eval+имя - единственно верное направление. 

А зачем тебе нужна возможность изменять "по указателю"? Ведь можно без лишнего сексу передать обьект и мя поля, благо всё в JS (да и в любом другом ECMA) навигируеться так (глобальные помним через window[name], по стандарту global[name]).

Nicholas_S, зачем доставать код closure/функции? Вдруг она привязана контекстом к какому нибудь окружению, а обратив всё в текст мы теряем контекст, т.е. теряем вообще смысл и мощь closure.

P.S. парни, я понимаю что праздники, но харе столько пить! smile 

Автор: Nicholas_S 22.12.2006, 14:48
Sardar, была сразу мысль сделать через addHandler(), но глядя на пример ушел в другую сторону.
Насчет контекста полностью согласен.

Автор: 12345c 22.12.2006, 15:02
Я не думал, но надо было как-то передать указатель на обработчик события. Ничего, кроме имён, не получилось.

Вариант с аттачами событий увеличивает число параметров, как видно. Не большая, конечно, беда...

Про closure я думал, но непонятно, как его использовать. В общем, пока пользуюсь своим кодом (буду применять в соседней, ещё не созданной теме smile ).

Стиль письма - дело привычки. Мне, например "{", стоящие отдельно на строке, мешают. А по опросу, этот стиль даже более предпочтителен в обиходе, чем они же, прилепленные к оператору.

Автор: Sardar 22.12.2006, 18:30
Цитата(12345c @  22.12.2006,  14:02 Найти цитируемый пост)
Про closure я думал, но непонятно, как его использовать

createDispatcher() в примере как работают closure. Там создаёться в контексте вызова переменная handlers, которая будет своей для каждого диспетчера. closure вообще вещь мощная. Это по сути некий код, привязываемый к разному контексту. Сам же контекст может содержать ссылки на что угодно.

Язык разрабатыва(л)(ю) (теорию только), для эффективности разделил концепцию closure на именнованные блоки и собственно closure. Вышло удобно smile

Автор: Zeroglif 22.12.2006, 20:00
12345c

Возможно тебе пригодится http://barney.gonzaga.edu/%7Eamoore1/php/addEvent/ регистрации событий через свойства объекта. Просто ещё один самобытный пример.

Автор: Sardar 22.12.2006, 20:46
Кстати в этой теме сразу видим, как в JS (любом скриптовом языке) не хватает getters/setters (которые реализованы в JS от мозиллы, а также в Rhino в Java6... да вообще везде, кроме JScript в ИЕ). А перегрузка операторов вообще была бы супер, представь как в C#:
Код

window.onclick += my_handler;
window.onclick += my_another_handler;
...
window.onclick -= my_handler;


function Test {
    this.event = new HandlersList(); //тут бы нужен интерфейс Callable, дабы быть как Function
    .....

    this.event(event_object); //вызывает все handlers
}

var a = new Test();
a.event += my_handler;



P.S. впрочем JS как язык вообще довольно сильно отстаёт от "современных течений" smile

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)