![]() |
|
Модераторы: Sardar, Aliance |
![]()
|
|
| rcdimon |
|
||||||||||||||||||||||||||||
![]() Опытный ![]() ![]() Профиль Группа: Участник Сообщений: 766 Регистрация: 12.7.2004 Где: Москва Репутация: нет Всего: 2 |
Всем привет.
Хочу поговорить на тему тулкитов ) Под этим словом я подразумеваю некоторую JavaScript библиотеку, реализующую какой либой элемент интерфейса. По причине сильно ограниченного набора элементов управления в HTML (текстовые поля, чекбоксы и т.д.) сейчас многие программисты пытаются придумывать свои и эмулировать их работу на JS. Примеров тому множество: Палитры, списки множественного выбора, менюшки и т.д. Мне по долгу моей службы сейчас тоже приходится создавать такие элементы, а так же изучать существующие. К сожалению очень многие существующие тулкиты написаны по моему мнению очень плохо. Приминение их сопряжено с большим количеством трудностей. Так же я не люблю применять коммерческие продукты по причине их раздутости. Чаще всего мне необходим некий базовый набор функций, лишь один способ управления элементом и т.д., тогда как тулкит предлагает широчайший спект всяческих возможностей. В условиях сильной перегруженности страницы JavaScript кодом такие тулкиты начинают откровенно тормозить. Сейчас я хочу рассказать о том как это делаю я. Буду рад выслушать критику т.к. только в споре рождается истина. В качестве примера возьму одну из самых простых библиотек- Палитру в простейшей ее реализации. Для начала необходимо решить вопрос о том каким образом тулит будет появляться на странице. Что он будет из себя представлять и т.д. Лично мое мнение- лучше всего каждый элемент представлять в виде объекта. Значит нам необходимо создать класс описывающий палитру. В этом классе должны быть методы для отрисовки палитры и все что связано с управлением. Тулкит должен появляться не в произвольном месте страницы, а точно там, где это необходимо пользователю. Следовательно у тулкита должен быть родительский элемент, в который он будет встраиваться.
Тут конструктору передается единственный параметр- id родительского элемента. Того самого, в который палитру необходимо встроить. А так выглядит сам конструктор. (Я использую библиотекуprototype)
Назначение всех переменных я объясню по ходу описания библиотеки. Сразу хочу сказать что я выбрал способ конструирования класса через prototype, то есть
А не
Только из соображений экономии памяти. Если все методы класса находятся прямо в нет то тогда они будут находится в каждом его экземпляре. Если пользователь будет использовать сразу 100 палитр на странице то все методы будут находится в памяти в 100 экземплярах. КОнечно этого нельзя допускать. Благодаря особенностям языка JavaScript и реализации в нем ООП мы можем методы вынести за пределы класса и тогда все объекты будут использовать одни и те же методы. Так как контекст (this) в JS меняется это становится возможным. Теперь приступим к методу отрисовки палитры
Наш тулкит сам присоединяется к родителю, пользователю не нужно заботится об этом this.Parent.appendChild(this.MainDiv); Палитра наша состоит из Главного Div'а и Div'ов с цветами. Это CSS
Все. Значит объект палитры создали и отрисовали. Теперь надо заняться его реагированием на действия пользователя. Если быть точным то мы уже сделали несколько шагов в этом направлении. Мы добавили 2 обработчика событий к главному Div. Одно на движение мыши по нему, другое по щелчку
Прошу обратить внимание на то как я объявляю обработчик события. Я пользуюсь для этого возможностями библиотеки prototype Event.observe. Но еще пользуюсь и bindAsEventListener. Это чрезвычайно полезная возможность библиотеки prototype. Все мы знаем что при возникновении события вызывается обработчик (функция или метод класса) чаще в всего с контекстом элемента DOM порадившего событие (this указывает на элемент DOM) даже если это метод объекта он все равно не вызывается с контекстом объекта! Потому что связь между объектом и методом в JS чисто номинальная... Методы- это функции, которые все равно остаются сами по себе и вызывать их можно откуда угодно и в зависимости от того откуда они были вызваны меняются и контекст. Причем получить ссылку обратно на объект бывает очень сложно. Есть несколько способов для этого- например глобальная переменная.. В которую предварительно можно записать ссылку на объект, а в обработчике события потом использовать.. Но использовние глобальных переменных - это ужасный стиль программировнаия. Тем более этих объектов может быть много, следовательно нужен уже массив... Другой более элегантный способ заключается в возможности расширять объекты в JS. В JS каждая функция, каждый объект DOM - объект со своим набором методов и свойств. Причем мы можем как хотим расширять их набор. Так вот способ заключается в добавлении свойства объекту DOM которое указывает на объект. Но не во всех браузерах и не всегда в обработчике события контекст указывает на DOM элемент ) Следовально это работает не везде. А вот функция bindAsEventListener библиотеки Prototype позволяет связать конкретную функция с конкретным контекстом и тогда в независимости от чего бы то ни было она будет вызываться с установленным контекстом. Как раз на функцию-обработчик события мы поставим контекст- наш объект. При связывании Prototype создает новый экземпляр функции, не трогая саму функцию. Если написать так Event.observe(this.MainDiv, 'mousemove', this.MainDiv_mousemove.bindAsEventListener(this)); то потом невозможно будет отвязать обработчик события ) потому что удаляется он так Event.stopObserving(this.MainDiv, 'mousemove', this.MainDiv_mousemove); bindAsEventListener(this) - писать нельзя. А сама this.MainDiv_mousemove - это другой экземпляр функции отличный от того который был привязан при создании обработчика и следовально удалить так его не получится. Поэтому тут можно сделать только через переменную, как я это и сделал. Так вот. Вы можете меня спросить почему я все события прилепил к главному диву. Одно из них (mousemove) будет использоваться для изменения вида DIV'а цвета на котором находится курсор мыши, а второе (click) будет использоваться для регитсрации выбора пользователем цвета. Казалось бы события нужно привязывать к самим дивам цвета! Да, так можно... Но теперь представьте сколько будет обработчиков событий создано нашим тулкитом? На каждый цвет по 2... А каждый обработчик- это уникальный экземпляр функции... Очень много памяти уйдет зря. Поэтому я тут опять буду пользоваться возможностями библиотеки prototype. События по умолчанию всплывают в браузерах. От потомков к родителям. Следовательно если щелчок произошел по DIV'у цвета, который находится на главном диве, то событие дойдет и до главного дива... Но при этом свойство события, указывающее на элемент породивший событие все равно будет указывать на верхний див цвета ) Теперь подробнее сами обработчики событий
Для того чтобы найти див цвета породившего событие я пользуюсь функцией Event.findElement(event, 'div'); Теперь мы можем сказать что большая часть тулкита написана. Есть объект, есть его визуальное представление и есть логика его реакции на действия пользователя. О чем теперь надо подумать? конечно о том как будет использовать его пользователь. Удобнее всего для пользователя- указать тулкиту где ему появиться, создать его и назначить обработчик события на выбор цвета пользователем. Так что теперь самое время подумать над привязкой событий к нашему тулкиту. В сложных тулкитах их может быть множество, но у нас всего одно- выбор цвета пользователем. Происходит когда пользователь щелкает по цвету. Пользователь хочет чтобы при этом вызвалась какая-то его функция с некоторыми параметрами, среди которых будет и цвет, который выбрал пользователь. Удобно пользователю привязывать их так
Первый параметр- это строка с названием процедуры которую необходимо вызвать. Второй- пользовательские параметры которые надо ей передать. Если пользователю надо просто привязать какую-то обычную функцию то тут все просто. Но если ему нужно чтобы при выборе цвета палитра вызвала бы метод какого-то конктретного экземпляра какого-то другого пользовательского класса? Например есть один тулкит использует другой. Тут немного сложнее. Опять же все можно сделать через глобальные переменные. Но я опять же не хочу этого делать ) А сделаю это так
Тут появился третий параметр- это указатель на текущий объект. Объект метод которого нужно вызвать. А первый параметр не имя функции а имя метода класса. Второй параметр null потому что я в данном случае не хочу передавать никакие свои дополнительные параметры. А теперь метод палитры для установки палитре события
Проще некуда. Ну а теперь метод FireEvent реализующий как раз вызов внешней процедуры или метода внешнего класса
Он поддерживает как раз 2 случая- когда нужно вызывать метод класса и когда просто внешнюю функцию. В качестве параметров он передает ссылку на себя (на объект палитры) на случай если пользователь захочет получить доступ к объекту, цвет выбранный пользователем и его собственные параметры, которые он указал при создании обработчика. Все, наш тулкит палитры написан. Теперь пользователь может использовать его. Для этого он делает например такую страницу
Видите как просто ) А главное написанный нами тулкит очень универсален. Его можно использовать как просто на странице (как в привиденном примере) так и в рамках другого тулкита. Например мы пишем тулкит Меню настроек. И один из параметров настройки цвет, ругой шрифт, третий еще что ни будь. И когда пользователь хотет изменить настйройки цвета тулкит менюшки вызывает тулкит Палитры, настраивая при этом его так как ему надо. Вот например как эта же палитра используется в рамках другого тулкита. Этот код даю в качестве бонусТрека проэтому не хочу его описывать ) Но если вопросы будут- отвечу с удовольствем
Эту палитру я выкладываю в сеть по адресу http://neversleep.ru/trash/js/palette/palette.html Можете убедиться в ее работоспособности. Попробуйте применить ее у себя для того чтобы проверить ее универсальность. Жду критику и вопросы |
||||||||||||||||||||||||||||
|
|||||||||||||||||||||||||||||
| JSman |
|
|||
|
Опытный ![]() ![]() Профиль Группа: Участник Сообщений: 279 Регистрация: 10.7.2006 Репутация: нет Всего: 4 |
статья очень грамотная! находим единомышленников.
говоря о критике. ну в ходе моей практике я пришел к выводу, что 1) использование тэга script в body нужно избегать. по моему мнению, это считается плохим стилем(( тэг скрипта использовать только в секции head. 2) элемент можно вставить после загрузки страницы в конкретное место при определенной идиентификации этого "места" атрибутами class или id тэгов div или span. 3) и все-таки я приидерживаюсь того мнения, что графические элементы нужно преставлять во флеше.. |
|||
|
||||
| rcdimon |
|
||||||
![]() Опытный ![]() ![]() Профиль Группа: Участник Сообщений: 766 Регистрация: 12.7.2004 Где: Москва Репутация: нет Всего: 2 |
Ну это совсем просто ) Просто все что у меня написано в скрипте в боди засовываем в функцию в скрипте в Head и вызываем при окончании загрузки страницы ) Можно конечно и раньше при возможности и желании.
Ага. Я знаком с таким подходом. Prototype даже предлагает нам свои услуги для реализации этого подхода ) Например getElementsByClassName (может с ошибкой написал, пишу по памяти) Этот способ легко и приятно использовать если просто на странице нужно вставить заранее извествное число заранее известных элементов. Не нужно будет скрипт вообще писать, даже эти 2 строчки. Объем кода для реализации этого небольшой нужен. Поэтому считаю возможным включить его во все библиотеки, даже если это не всегда нужно )
Ну а тут я уже не могу согласиться ) Хотя спорить на эту тему не готов. |
||||||
|
|||||||
| JSman |
|
|||
|
Опытный ![]() ![]() Профиль Группа: Участник Сообщений: 279 Регистрация: 10.7.2006 Репутация: нет Всего: 4 |
ну я уже думаю, что нужно создавать чуть ли не отдельный раздел по созданию элементов=)) я раньше точно также писал в свое время свой так называемый manifest для веб странички.. ну и нормально вроде.. принимая внимание мою критику, твой код станет очень крутым))))
|
|||
|
||||
| rcdimon |
|
|||
![]() Опытный ![]() ![]() Профиль Группа: Участник Сообщений: 766 Регистрация: 12.7.2004 Где: Москва Репутация: нет Всего: 2 |
Ага! А еще я вообще предлагаю создать некий список рекомендаций по созданию элементов, написанный совместно со всеми желающими. Смысл этого документа будет в том, чтобы люди создавали совместимые продукты, и их было легко использовать у себя. Надеюсь к нам присоединится еще что ни будь |
|||
|
||||
| SamDark |
|
|||
|
Добрый кот ![]() ![]() ![]() Профиль Группа: Участник Сообщений: 1424 Регистрация: 25.7.2006 Где: Voronezh Репутация: нет Всего: 38 |
Присоединится не против. Весь свой JS пишу примерно в таком стиле.
Последний раз это был структурированный дропбокс с постраничной разбивкой. Привязывался автоматом ко всем <input class="widget-dropbox">. Единственное, что не удалось - сделать какое-то кэширование. т.к. данные менялись довольно часто, пришлось перестраивать вёрстку бокса на их основе при каждом действии. -------------------- rmcreative.ru — Это жжж неспроста... yiiframework.ru — О фреймворке Yii на русском. reggi — здесь я регистрирую домены |
|||
|
||||
| JSman |
|
|||
|
Опытный ![]() ![]() Профиль Группа: Участник Сообщений: 279 Регистрация: 10.7.2006 Репутация: нет Всего: 4 |
Первое правило - минимум кода =) Второе - никогда не делайте так window.onload = function () {...} А вообще все приходит к использованию общей библиотеки. Единственной проблемой является так называемая строгость порядка вставки библиотек. это нужно решить. вставка элементов делится на 2 категории: 1. замена стандартных элементов. 2. вставка собственных. при этом, как упоминалось, будем иметь дело только с тэгами div и span. Это сообщение отредактировал(а) JSman - 21.7.2007, 18:00 |
|||
|
||||
| dXdYdZ |
|
|||
|
Шустрый ![]() Профиль Группа: Участник Сообщений: 146 Регистрация: 16.4.2007 Репутация: нет Всего: 4 |
Недостаток Вашего подхода: при загрузке грузится слишком много javaScript-кода(хотя бы весь Prototype, без которого вполне можно обойтись). Кроме этого саму палитру тоже можно было бы написать попроще.
Преимущество: чёткий подход к созданию компонент. Сейчас пишу свою javaScript-библиотеку (http://forum.vingrad.ru/forum/topic-104881.html). У меня тоже есть чёткий подход к созданию компонент, но он несколько отличается от Вашего. Это сообщение отредактировал(а) dXdYdZ - 2.8.2007, 15:41 |
|||
|
||||
| SamDark |
|
|||
|
Добрый кот ![]() ![]() ![]() Профиль Группа: Участник Сообщений: 1424 Регистрация: 25.7.2006 Где: Voronezh Репутация: нет Всего: 38 |
dXdYdZ,
А посмотреть на это чудо можно? -------------------- rmcreative.ru — Это жжж неспроста... yiiframework.ru — О фреймворке Yii на русском. reggi — здесь я регистрирую домены |
|||
|
||||
| Zeroglif |
|
|||
|
Опытный ![]() ![]() Профиль Группа: Участник Сообщений: 644 Регистрация: 22.9.2005 Репутация: нет Всего: 66 |
||||
|
||||
| SamDark |
|
|||
|
Добрый кот ![]() ![]() ![]() Профиль Группа: Участник Сообщений: 1424 Регистрация: 25.7.2006 Где: Voronezh Репутация: нет Всего: 38 |
Zeroglif,
Prototype писался для Ruby-программистов с целью привести JS к Ruby. Всё оттуда... -------------------- rmcreative.ru — Это жжж неспроста... yiiframework.ru — О фреймворке Yii на русском. reggi — здесь я регистрирую домены |
|||
|
||||
| dXdYdZ |
|
|||
|
Шустрый ![]() Профиль Группа: Участник Сообщений: 146 Регистрация: 16.4.2007 Репутация: нет Всего: 4 |
Сейчас я описываю созданные компоненты (описание выкладываю на посвящённый данной библиотеке сайт) и параллельно исправляю всякие мелкие глюки, ошибки и т. д. Описал примерно 40%. Когда я всё это доделаю, собираюсь выложить сайт библиотеки (который, кстати, тоже делаю с использованием написанных мной компонент) в интернет. А сейчас там почти нет никакого дизайна и есть глюки, поэтому покажу, когда всё исправлю. Кстати, я обошёлся без всякого prototype и без дополнительных библиотек. Только благодаря этому и удаётся достичь небольшого размера и высокой скорости. Это сообщение отредактировал(а) dXdYdZ - 2.8.2007, 18:43 |
|||
|
||||
| SamDark |
|
|||
|
Добрый кот ![]() ![]() ![]() Профиль Группа: Участник Сообщений: 1424 Регистрация: 25.7.2006 Где: Voronezh Репутация: нет Всего: 38 |
dXdYdZ,
Без прототипного наследования или без библиотеки? -------------------- rmcreative.ru — Это жжж неспроста... yiiframework.ru — О фреймворке Yii на русском. reggi — здесь я регистрирую домены |
|||
|
||||
| dXdYdZ |
|
|||
|
Шустрый ![]() Профиль Группа: Участник Сообщений: 146 Регистрация: 16.4.2007 Репутация: нет Всего: 4 |
Без библиотеки. А наследование я реализовал тоже немного своеобразно. Пример компонента - всплывающая подсказка:
Это и всё, что понадобилось для создания соответствующего компонента. |
|||
|
||||
| SamDark |
|
|||
|
Добрый кот ![]() ![]() ![]() Профиль Группа: Участник Сообщений: 1424 Регистрация: 25.7.2006 Где: Voronezh Репутация: нет Всего: 38 |
dXdYdZ,
А про пожирание памяти подумали? -------------------- rmcreative.ru — Это жжж неспроста... yiiframework.ru — О фреймворке Yii на русском. reggi — здесь я регистрирую домены |
|||
|
||||
![]()
|
| 0 Пользователей читают эту тему (0 Гостей и 0 Скрытых Пользователей) | |
| 0 Пользователей: | |
| « Предыдущая тема | JavaScript: Применение библиотек | Следующая тема » |
|
|
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности Powered by Invision Power Board(R) 1.3 © 2003 IPS, Inc. |