Показаны сообщения с ярлыком javascript. Показать все сообщения
Показаны сообщения с ярлыком javascript. Показать все сообщения

четверг, июля 10, 2008

сладкий функциональный макрос в яваскрипт

как известно, создавать на яваскрипте короткие инлайн колбэк функции - не слишком приятное занятие - получается много кода со множеством скобочек.

приведу классический пример:


Array.prototype.each= function( callback ){
for( var i= 0, len= this.length; i < len; ++i )
callback.call( this, this[ i ], i );
};

var summ= 0;
[-1,1,2,3].each( function( val, key ){ summ+= val } );
alert( summ );


тут в метод итерирования передаётся функция замкнутая на текущий контекст, которая должна выполнить нужные нам операции для каждого элемента массива. довольно многословно, не правда ли? было бы замечательно хотябы сделать короткий алиас для ключевого слова function, но яваскрипт ничего такого не позволяет -_-

единственное возможное решение в таком случае - использовать какой-либо препроцессор. но завязываться на серверную или оффлайновую предобработку как-то не очень хочется. а если помещать код в яваскрипт в виде строки, то мы теряем подсветку синтаксиса. тобишь необработанный код должен сохранять синтаксическую совместимость с яваскриптом.

что ж, решение есть и довольно красивое... вот простая функция, динамически меняющая исходный код переданной в качестве аргумента функции:


var $func= function( func ){
eval(
'func=' + String( func )
.replace( /\{\$:/g, 'function($){return ' )
);
return func;
};


а вот пример использования:


$func( function( ){

Array.prototype.each= function( callback ){
for( var i= 0, len= this.length; i < len; ++i )
callback.call( this, { key: i, val: this[ i ] } );
};

var summ= 0;
[-1,1,2,3].each( {$: summ+= $.val } );
alert( summ );

})();


тут весь код приложения засовывается в функцию, пропускается через препроцессор и потом исполняется. при этом внутри можно создавать инлайн-функции используя простой и элегантный синтаксис:


{$: /* какой-то код,
результат выполнения которого будет возвращён функцией */ }

понедельник, ноября 26, 2007

что может быть хуже чем показывать и смотреть рекламу? отлавливать в ней ошибки!

повесили на один сайт google adsense. в мозилле, опере всё пучком, а ИЕ что-то кортачится - ничего не показывает кроме ошибок: "незавершённая строковая константа" и "недопустимый символ". в качестве адреса указывает нашу страницу. номер строки и символа - циркулирует между пятком вариантов.

если бы эта проблема наблюдалась только в мозилле - я бы взял огнебаг, да отдебажил по самое небалуйся, но под ИЕ, к сожалению, ничего подобного нет (-_-) причём ошибка выскакивала только в шестом ИЕ. седьмой же - не возникал и спосойно отображал рекламу. правда русские буквы он отображал исключительно вопросительными знаками. сначала я не обратил на это особого внимания, но как потом оказалось - зря.

страницы у нас отдаются в utf-8, но гугол свою рекламу почему-то отдаёт в windows-1251. подозреваю он определяет язык (по региону или по http-заголовку, не суть в общем) в соответствии с ним отбирает объявления (из 6 ссылок 1-2 на русском языке) и по неизвестной причине выбирает кодировку 1251. ие в заголовках её не запрашивает, исходная страница в utf-8, видимо гугол просто для русского языка по дефолту шлёт 1251 и тем самым совершенно не учитывает поведение детища мелкософта - эта мохнатая скотина плевать хотела на http-заголовки подгружаемого яваскрипта. ие при загрузке скриптов и стилей устанавливает для них ту же кодировку, что и на странице для которой они загружаются. как следствие, ие думал, что подгружаемый гуглоскрипт с рекламными данными был в кодировке utf-8 и встретив там русские буквы, которые не входят в ASCII-7, получал совершенно невообразимые коды символов.

как обойти такое поведение ИЕ? да очень просто! есть два варианта:
1. отдавать скрипты в той же кодировке, что и исходная страница.
2. указать в тэге <script> атрибут charset содержащий код нужной кодировки.
если меня читают товарищи из гугла, то в этот их скрипт нужно добавить:
document.write('<script language="JavaScript1.1" 
charset="windows-1251" src="'+c+'"><\/script>')
естественно вместо "windows-1251" нужно подставить ту кодировку в которой собираемся грузить данные.

да, и отдельное спасибо за то, что мне пришлось копаться в этой лапше (-_-)

вторник, октября 30, 2007

операция прервана высшими силами

недавно столкнулся с такой проблемой в IE: при переходе на следующую страницу он выдаёт алерт "не удалось открыть узел... операция прервана" и отказывается грузить страницу. ctrl+f5 обычно спасает положение. так и не понял почему это происходит, но связано это с тем, что скрипт пытается работать с dom в то время как его уже/ещё не существует.

суббота, октября 27, 2007

объекты, хэш-таблицы и яваскрипт

как известно, всё в яваскрипт является объектами.

"как это всё?" - спросит подкованный читатель, - "а как же примитивные типы?"
а примитивные типы - тоже объекты, только несколько иные...

разберёмся с терминологией:
объект - понятие довольно абстрактное, означающее "некая сущность обладающая состоянием и поведением".
примитивные типы в яваскрипте безусловно обладают и состоянием (собственно значение) и поведением (методы, которые они наследуют от своего прототипа)

пытливый читатель может тут возразить, что на самом деле в случае вызова метода примитивный тип просто конвертится в объект, который уже наследует методы. и будет прав! но это не отменяет того факта, что метод-то мы вызываем для переменной содержащей примитивное значение и от состояния этого значения может быть вызван разный метод и дать, соответственно, разный результат.

каждый примитивный тип имеет своё уникальное поведение: при попытке доступа к полям они конвертятся в свои типы объектов и наследуют соответствующий набор полей от прототипа. значит примитивные типы - тоже объекты.

но что в них такого особенного?

в отличие от остальных объектов примитивне объекты не являются хэш-таблицами, то есть их состояние определяется одним единственным безымянным полем. им нельзя добавить дополнительных полей (в том числе и содержащих функции), но они могут отнаследовать их от прототипа. для доступа к полям при этом используется скрытый автоматически создаваемый объект-посредник, ибо примитивные объекты не могут иметь именованных полей.

основной недостаток яваскрипта заключается в том, что переменная this внутри методов указывает на объект-посредник, а не на примитивный объект, в результате чего внутри метода мы не можем узнать является ли объект, в контексте которого происходит вызов, самостоятельным объектом или объектом-посредником. а это бывает важно в свете того, что передача полноценных объектов происходит по ссылке, а примитивных - по значению.

ps: вот что действительно не является объектами, так это значения null и undefined. и это очень печально (-_-)

вторник, сентября 18, 2007

установка обработчиков событий в javascript

Я уже говорил, что люблю яваскрипт? Если нет, то: "лучше языка я ещё не встречал!". Есть, конечно и недостатки, но многие из них можно обойти...
Но сейчас пойдёт речь не о недостатках, а наоборот, об одном элегантном решении...

Самый простой способ повесить обработчик события - это просто написать:
node.onclick= function( e ){
if( !e ) e= window.event;
alert( e );
}

Метод довольно простой и наглядный, но основной его недостаток - для одного события у одного узла можно назначить только один обработчик, что очень неудобно.

Более сложный способ, лишённый этого недостатка, - это воспользоваться методами addEventListener (DOM) и attachEvent (IE). Например: http://php.ru/forum/viewtopic.php?t=7374

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

И тут мы подходим к самому главному! В яваскрипте любая функция является объектом, а значит она может иметь дополнительные свойства. В частности, можно задать каким-нибудь свойством массив обработчиков, а функция должна просто пройтись по каждому из них и запустить. В итоге у меня получился такой код:
node.observe= function( event, func ){
if( !node[event] ){
node[event]= function f( e ){
if( !e ) e= window.event;
var funcs= node[event].funcs;
for( var i= 0; i < funcs.length; ++i ){
if( funcs[i] ) funcs[i]( e );
});
};
node[event].funcs= [];
}
var funcs= node[event].funcs;
for( var i in funcs )
if( funcs[i] == func ) return;
funcs.push( func );
}
node.unobserve= function( event, func ){
var funcs= node[event].funcs;
for( var i in funcs )
if( funcs[i] == func )
delete funcs[i];
}

то есть, мы расширяем html-ноду ( хотя, в ИЕ наврятли удастся расширить что-то отличное от html-елемента -_- ну да не суть ) двумя функциями, одна из которых добавляет обработчик, а вторая - удаляет.

Примеры использования:
function ping( e ){
alert( e );
}

node.observe( 'onclick', ping );

node.unobserve( 'onclick', ping );

node.observe( 'onmyevent', ping );

node.onmyevent( { keyCode: 1 } );


upd: кстати, на событие 'onbeforeunload' и возможно на ещё какие-то нельзя вешать обработчики с помощью addEventListener/attachEvent, ибо некроссбраузерно.

upd2: мля... не работает в мозилле в designMode... придётся оной подпорки делать...

понедельник, сентября 10, 2007

одна большая дырка

правильно, речь пойдёт об ИЕ ^_^
недавно (ну, месяца два назад) c удивлением обнаружил, что в ИЕ работает даже такая конструкция:

<img src="javascript:alert(1)" />

что тут можно сказать? только сабж ^_^

пятница, сентября 07, 2007

выделение, визивиг и изменение контента

при реализации визивига некоторые вещи приходится вытворять по схеме: сериализация->обработка->десериализация

в качестве примера - реализация динамической подсветки кода. одними только dom-функциями это реализовывать - страшный геморрой, а ещё и очень медленно. гораздо быстрее и проще пройтись парой регулярок по сериализованному документу.

но тут нас подстерегает одна гадость - после обработки хорошо было бы восстановить выделение или хотябы позицию курсора. очевидно, пока выделение ещё существует, нужно сохранить его позиции в dom-е, обработать и по оставленным меткам восстановить выделение.

очевидно, что метки начала и конца выделения должны быть неиспользуемой последовательностью символов юникода. также они должны иметь нулевую ширину, дабы при вводе текста не было скачков. я остановился на парах диактрических знаков: '\u0300\u0301' и '\u0302\u0303'. вроде как маловероятно, чтобы в тексте потребовалось их применить последовательно. ширины они точно не имеют. единственный недостаток: они имеют визуализацию, но с учётом того, что обработка должна быть "мгновенной" (ибо рилтаймовая подсветка) - кратковременное появление маленьких значков не должно быть заметно на глаз.

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

в ИЕ всё оказалось весьма шоколадно...

сохранение меток выглядит так:

var range= document.selection.createRange();
range.pasteHTML( '\u0300\u0301' + range.htmlText + '\u0300\u0301' );

восстановление несколько менее тривиально:

var ran= document.body.createRange();
var tr= document.body.createRange();
tr.findText( '\u0300\u0301' );
tr.text= '';
ran.setEndPoint( 'StartToStart', tr );
tr.findText( '\u0302\u0303' );
tr.text= '';
ran.setEndPoint( 'EndToEnd', tr );
ran.select( );

а вот реализуя то же самое для остальных браузеров пришлось несколько попотеть...

сохранение меток:

var ran= window.getSelection().getRangeAt(0);
var rane= ran.cloneRange( );
rane.collapse( false );
var node= document.createTextNode( '\u0302\u0303' );
rane.insertNode( node );
var node= document.createTextNode( '\u0300\u0301' );
ran.insertNode( node );

а для восстановления соответственно приходится бежать по всему дому в поисках открывающей и закрывающей пар node+offset, после чего наведение выделения с помощью функций setStart и setEnd. почему w3c не соизволила узаконить findText остаётся загадкой 8(o_0)8

суббота, июля 07, 2007

Отладка скриптов в браузерах


Mozilla:

https://addons.mozilla.org/ru/firefox/addon/1843 - firebug - думаю в представлении не нуждается ^_^ маст хэв
https://addons.mozilla.org/ru/firefox/addon/60 - web developer - специальная панелька с большим количеством полезных в быту разработчика функций.
https://addons.mozilla.org/ru/firefox/addon/249 - html validator - позволяет быстро провести валидацию документа как с помощью tidy, так и с помощью sgml парсера.
https://addons.mozilla.org/ru/firefox/addon/3939 - relax validator - ещё не пробовал, но не упомянуть не мог ^_^'

Opera:

http://dev.opera.com/tools/ - жмём Developer Console, после чего в опере появляется кнопочка, по нажатию на которую открывается окошко со всякой инфой. жалкое подобие огненного бага, конечно, но хотябы что-то...

IE:
очень длинная ссылка - чувствуется работа дизайнера. не смотря на присутствие функций отсутствующих в огненном баге, по общим показателям всё же менее функциональна. зато опрятна и удобна. к сожалению возможности просмотра DOM-а у неё нет (-_-)
http://www.blunck.info/iehttpheaders/iehttpheaders.html - просмотр заголовков
весьма мощный аналог фаербага

Safari:
ctrl+shift+j открывает консоль ошибок.

среда, мая 23, 2007

Продолжая тему Аякса...

То есть продолжная эту тему...

теперь на странице достаточно вставить ссылку, задать для неё соответствующий класс и вместо неё будет подгружен контент из другого файла:

<a class="j-frame" href="rightside.xml">правое меню</a>

более того, подгружать контент можно и динамически:

jFrameLoad($('element-id'),URI);

первым параметром идёт ссылка на элемент, вторым - ссылка на документ.

пример реализации тут: http://dark-demon.nm.ru/web/samples/xhtml/index.xml#maintemplate

среда, мая 16, 2007

свежий взгляд на AJAX

сегодня, наконец, сделал давно задуманную фичу: на одной странице находится фрейм, в него грузится другая страница, которая после загрузки лезет к родительскому фрейму и заменяет его на див в который пихает всё своё содержимое. в результате получается, как буд-то бы данные из второй страницы изначально были в первой. вот такой вот интересный AJAX получился (^_^)
ссылка всё та же: http://dark-demon.nm.ru/web/samples/xhtml/index.xml#maintemplate (правый сайдбар именно так и вставлен)
весь скрипт реализующий этот аякс занимает всего ничего:
if (fr= window.frameElement) {
if (isClass(fr,'j-autoreplace')) {
var el= fr.ownerDocument.createElement('div');
el.innerHTML=
document.getElementsByTagName('body')[0].innerHTML;
fr.parentNode.replaceChild(el,fr);
}
}
это ультракомпактно по сравнению с конкурентами (^___^)

среда, мая 09, 2007

XHTML в массы!

Демка моего XHTML+XSLT+CSS+JS фреймворка с пояснениями: http://dark-demon.nm.ru/web/samples/xhtml/index.xml

Поскольку своего движка на PHP я ещё не написал, комменты постим здесь, на этом кривом блоге.