О делегировании в DOM
Скажу честно, нахожусь в долгом свободном поиске идеальной библиотеки web-компонентов, пересмотрел большое количество оных, многие, мне как начинающему, с первого взгляда казались очень громоздкими, другие слишком сложные, третьи...
Для меня идеал - браузерная javascript библиотека по структуре похожая на .Net. Таковой нет. Но в последнее время стал думать о компонентах на основе делегирования, на такие мысли меня навела библиотека CornerJS, к сожалению, автор не достаточно описал свою библиотеку, но идею я понял и решил создать "велосипед" на свой лад, писал код около 2-х часов, возможны ошибки и недочеты, т.к. это не prodaction, а просто пример делегированных компонентов. Основные директивы подхода: 1. Все обработчики "вешаются" на body, ну разумеется, в целях производительности, mousemove "вешать" не стоит. 2. Объекты-делегаторы не вешают своих обработчиков, главный объект, при наличии события ищет делегатор и пытается вызвать у него соответствующий событию метод. 3. Главный объект умеет создавать новый dom-компонент из шаблона, при создании, вызывает у его делегатора (и вложенных) метод init(). Плюсы: - малое количество обработчиков, все "вешаются" на body - встроенное делегирование событий - делегаторы, при желании, можно в прямом смысле наследовать через прототип - использование шаблонов разметки, можно придумать механизм предварительной обработки шаблонов (вставки, псевдо-наследования, сложения аттрибутов ...) - легкое создание и удаление компонентов из dom - мизерный движок - IE8 отдыхает Минусы: - возможны тормоза, связанные с обработкой событий, обусловлено поиском делегатора по dom и необходимостью "всплытия" события до body, но это надо проверить - сложность реализации модульности, компонент состоит из 3-х частей: стиля, разметки и самого js кода, придется думать, как все это "запихать" в один js-файл - IE8 отдыхает Предлагаю обсудить подход и реализацию. Ну вот и сам код, пример окна диалога: <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title>Delegation</title> <style> [template] { display:none; } .dialog { position:absolute; left:100px;top:100px; width:300px;height:250px; border:1px solid gray; background-color:#fff; } .dialog > .container { position:relative; top:0px;left:0px;right:0px;bottom:0px; } .dialog > .container > .close-button { position:absolute; right:3px;top:3px; } .dialog > .container > .title { cursor:default; position:absolute; top:0px; left:0px;right:0px; height:20px; line-height:20px; padding-left:20px; overflow:hidden; background-color:#C7E2FA; border-bottom:1px solid gray; } .dialog > .container > .content { position:absolute; top:20px;bottom:0px; left:0px;right:0px; } </style> </head> <body> <button onclick='add_dialog()'>Нажми</button> <script> function add_dialog(){ Delegator.create( 'dialog' ); } </script> <script> var Delegator = { events:['click','mousedown','mouseup'], start:function(){ var _this = this; var handler = function(ev){ return _this.on_event(ev); } for( var i=0,l=this.events.length; i<l; i++ ){ document.body.addEventListener( this.events[i], handler, false ); } }, on_event:function(ev){ console.log( ev.type ); var top = document.body; var target = ev.target; var type = ev.type; var delegate_target = null, action_target = null, value_target = null; var delegate = null, caction = null, cvalue = null; // поиск делегатора, а за одно действие caction и данные cvalue do{ if( !cvalue && target.hasAttribute('cvalue') ){ cvalue = target.getAttribute('cvalue'); value_target = target; } if( !caction && target.hasAttribute('caction') ){ caction = target.getAttribute('caction'); action_target = target; } if( target.hasAttribute('delegate') ){ delegate = target.getAttribute('delegate'); delegate_target = target; break; } }while( target != top && ( target = target.parentElement ) ); if( delegate ){ if( this.delegators[delegate] ){ var obj = this.delegators[delegate]; // дополнение события своими данными ev.delegate = delegate; ev.delegate_target = delegate_target; ev.caction = caction; ev.action_target = action_target; ev.cvalue = cvalue; ev.value_target = value_target; var method = null; var method1 = 'on_'+type; // например on_click var method2 = caction ? ( method1+'_'+caction ) : null; // например on_click_title var method3 = ( method2 && cvalue ) ? ( method2+'_'+cvalue) : null; // например on_click_button_close if( typeof(obj[method3]) == 'function' ){ method = method3; }else if( typeof(obj[method2]) == 'function' ){ method = method2; }else if( typeof(obj[method1]) == 'function' ){ method = method1; } if( method ){ return obj[method]( delegate_target, ev ); } }else{ console.log('Не найден делегатор '+delegate); } } return false; }, delegators:{}, create:function( template , parent_element ){ parent_element = parent_element || document.body; var element = document.querySelector('[template='+template+']'); if(element){ element = element.cloneNode(true); // подмена на клон element.removeAttribute('template'); // удаляем флаг шаблона parent_element.appendChild( element ); var all=[]; // запись всех делегируемых элементов в один массив if( element.hasAttribute('delegate') ) all.push( element ); var nodes = element.querySelectorAll('delegate'); if( nodes && nodes.length ) for( var i=0,l=nodes.length; i<l; i++ ) all.push(nodes[i]); // поиск метода init и запуск for( var i=0,l=all.length; i<l; i++ ){ var node = all[i]; var delegate = node.getAttribute('delegate'); if( this.delegators[delegate] ){ var obj = this.delegators[delegate]; if( typeof(obj.init) == 'function' ){ obj.init( node ); } }else{ console.log('Не найден делегатор '+delegate); } } }else{ console.log('Не найден шаблон '+template); } } } Delegator.start(); </script> <div template='dialog' class='dialog' delegate='dialog'> <div class='container'> <div class='title' caction='title' >title</div> <div class='content'>Container <hr /> <button caction='button' cvalue='add'>Добавить дочернее окно</button> </div> <button caction='button' cvalue='close' class='close-button'>X</button> </div> </div> <script> /* делегатор для dialog */ Delegator.delegators.dialog = { init:function( element ){ console.log('Элемент dialog создан'); this.to_top( element ); }, on_mousedown_title:function( element, ev ){ this.to_top( element ); var dx = ev.pageX - element.offsetLeft; var dy = ev.pageY - element.offsetTop; var move = function( ev ){ element.style.left = ( ev.pageX - dx ) + 'px'; element.style.top = ( ev.pageY - dy ) + 'px'; return false; } var stop = function( ev ){ document.removeEventListener( 'mousemove' , move , true ); document.removeEventListener( 'mouseup' , stop , true ); element = move = dx = dy = stop = null; return false; } document.addEventListener( 'mousemove' , move , true ); document.addEventListener( 'mouseup' , stop , true ); return false; }, on_click_button_close:function( element, ev ){ this.destroy( element ); return false; }, on_click_button_add:function( element ){ Delegator.create( 'dialog' , element.querySelector('.container') ); return false; }, on_click:function( element , ev ){ this.to_top( element ); return false; }, to_top:function(element){ var parent = element.parentElement; if( !parent.hasAttribute('dialog-z-index') ){ parent.setAttribute('dialog-z-index',10); } var z = 1 + parseInt(parent.getAttribute('dialog-z-index')); parent.setAttribute('dialog-z-index',z); element.style.zIndex = z; console.log( z ); }, destroy:function(element){ element.parentElement.removeChild( element ); } } </script> </body> </html> |
Цитата:
Мне почему то кажется, что тебя заклинило на слове "делегирование" :) Если уж взялся писать на яваскрипте, будь любезен, пиши на нем так, как принято в яваскрипте. Глаза режет: Цитата:
Цитата:
Цитата:
Цитата:
|
Цитата:
По "эволюции" веба, polymer как раз и "запихали" все 3 составляющие (CSS,HTML,Javascript) в один файл, который подгружается с помощью HTMLImports (HTMLImports не поддерживаются браузерами, кроме хрома). Идея хорошая, но очень "тяжелая". Про резь в глазах, не совсем понял. Я просто не очень люблю CamelCase, а само название метода on_click_button_close уже несет информацию и легко читается, считаю главным придерживаться выбранного стиля во всем проекте. |
Цитата:
|
Дело не в "люблю/не люблю". На каждом яп следует писать так, как общепринято именно для этого языка.
|
Часовой пояс GMT +3, время: 10:20. |