<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Чудо{вищные} заметки &#187; web</title>
	<atom:link href="http://miracle.rpz.name/category/web/feed/" rel="self" type="application/rss+xml" />
	<link>http://miracle.rpz.name</link>
	<description>Sorry for my terrible english. My native language is PHP.</description>
	<lastBuildDate>Thu, 12 Jan 2012 20:42:08 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.4-alpha-19719</generator>
		<item>
		<title>Асинхронные задачи в PHP</title>
		<link>http://miracle.rpz.name/2011/04/16/asynchronous-jobs-with-pure-php/</link>
		<comments>http://miracle.rpz.name/2011/04/16/asynchronous-jobs-with-pure-php/#comments</comments>
		<pubDate>Sat, 16 Apr 2011 19:59:54 +0000</pubDate>
		<dc:creator>MiRacLe</dc:creator>
				<category><![CDATA[dev]]></category>
		<category><![CDATA[php]]></category>
		<category><![CDATA[web]]></category>

		<guid isPermaLink="false">http://miracle.rpz.name/?p=450</guid>
		<description><![CDATA[Не открою Америки, если скажу что порой требуется выполнить некую времязатратную операцию, результат которой либо не нужен пользователю вовсе (запись в лог, удаление временных файлов и другое обслуживание сервера), либо его можно обмануть и сказать что операция выполнена успешно, а саму операцию выполнить &#8220;попозже&#8221;. Самым наверное близким всем примером такой операции можно назвать отправку почты [...]]]></description>
			<content:encoded><![CDATA[<p>Не открою Америки, если скажу что порой требуется выполнить некую времязатратную операцию, результат которой либо не нужен пользователю вовсе (запись в лог, удаление временных файлов и другое обслуживание сервера), либо его можно обмануть и сказать что операция выполнена успешно, а саму операцию выполнить &#8220;попозже&#8221;. Самым наверное близким всем примером такой операции можно назвать отправку почты &#8211; smtp-cессия может длится довольно долго, особенно если письмо огромное, сервер тормозной (ну да вы сами всё знаете, в реальном мире великое множество острых углов), но зачем пользователю ждать результата? Что ему делать если результат неуспешный? &#8220;Попробуйте повторить операцию позже&#8221; ? Не смешно! Рядовой пользователь на ваш рядовой ресурс не вернётся, дотошный &#8211; свяжется с вами другими способами, так что можно с чистой совестью соврать ему &#8211; мол всё путём, всё отправлено, всё доставлено, записано и всё так успешно и идеально, а в это время потихоньку начать на самом деле выполнять задачу.</p>
<p>Конечно если вокруг вас крутятся тысячи серверов, слово &#8220;ынтырпрайз&#8221; для вас звучит буднично, то для вас уже изобретено много-много buzz-word-ных решений с очередями сообщений, очередями задач и другими полезными решениями, но львиная доля разработчиков всё-таки создают сайты-визитки, поддерживают сайты на хостинг-планах &#8220;всё по 20 рублей&#8221;, да и вообще на мой взгляд глупо <a href="http://highload.com.ua/index.php/2010/07/09/gearman-и-php-асинхронные-задачи/">для отправки почты окружать простейший php-скрипт кучей софта вроде gearmand + расширения для работы с ним</a>. Я же хочу показать решение &#8220;для бедных&#8221;, <strong>простое, но удобное в разработке, поддержке и отладке решение</strong>.</p>
<p>Итак задача &#8211; отправить письмо, не заставляя ждать пользователя.<br />
Имеем:</p>
<pre class="php">
list($recipient,$subject,$body) = get_vars_from_request();
include 'superpupermailer.php';
$mailer = new SuperPuperMailer($recipient,$subject,$body);
if ($mailer->send()) {
echo "Аллилуя! Мы сделали это, храни нас Великий Байт.";
} else {
echo "О нет, это случилось!!! Быть того не может...но всё же случилось - приходи, милый друг, в другой раз, а сейчас ошибка!";
}
</pre>
<p>Тут всё понятно &#8211; либо отправилось, либо не отправилось &#8211; всем приходилось видеть это с разных сторон, те кто видел это со стороны браузера нередко наблюдали не только &#8220;ошибочка вышла&#8221;, но и другие подробности вроде конкретных строк в скриптах, warning-ов и Fatal error-ов. Но мы уже выяснили ранее &#8211; во-первых пользователю совершенно по барабану что у вас произошло с сервером, а во-вторых &#8220;пробовать ещё раз&#8221; он скорее всего не будет. Поэтому код можно изменить следующим образом:</p>
<pre class="php">
list($recipient,$subject,$body) = get_vars_from_request();
$async_job = '<?php include "superpupermailer.php"; $mailer = new SuperPuperMailer("'.$recipient.'","'.$subject.'","'.$body.'");
return $mailer->send();';
if (file_put_contents('/dir/for/jobs/email.php',$async_job)) {
echo "Ура! Мы сделали этот мир лучше!";
} else {
echo "Увы, мир жесток и безжалостен...";
}
</pre>
<p>Что это и зачем? Где отправка почты? Каким образом письмо отправится? Больше вопросов чем ответов. Опять-таки остался <em>else</em>, шило поменяли на мыло? В целом да &#8211; заменили тёплое мягким, но всё-таки нет &#8211; как часто у вас заканчивается неудачей запись на диск? Чаще чем таймаут при коннекте к почтовому серверу? </p>
<p>Теперь о главном &#8211; зачем файл? Где отправка почты?</p>
<p>Этим займётся другой скрипт:</p>
<pre class="php">
if (true === include('/dir/for/jobs/email.php')) {
  unlink('/dir/for/jobs/email.php');
}
</pre>
<p>Осталось вызвать этот скрипт. Например так:</p>
<pre class="php">
...
echo 'Ура! Мы сделали этот мир лучше!<img src="/job.php" height="1" width="1">';
...
</pre>
<p>Или же добавим в cron задание на вызов job.php каждый час/пять минут/каждую минуту.</p>
<p>Всё. Мы <strong>не заставили пользователя ждать</strong>, мы отправили сообщение, мы молодцы. А если не отправили? Отправим потом &#8211; файл-то остался!</p>
<p>Конечно возникает много закономерных возражений &#8211; что будет если send.php (или job.php) будет вызван одновременно двумя пользователями?<br />
Эти вопросы надо обстоятельно решать, задачи создавать с уникальными именами, блокировать одновременный запуск скрипта job.php, в общем работы аж на целых десять минут.</p>
<p>Самое интересное в конце:</p>
<pre class="php">
...
$job_name = uniqid('mail_');
if (file_put_contents('/dir/for/jobs/'.$job_name.'.php',$async_job)) {
   echo "Ура! Мы сделали этот мир лучше!";
              $job_url = '/job.php?job='.$job_name;
              if (false !== ($fh = @fsockopen($_SERVER['SERVER_ADDR'], $_SERVER['SERVER_PORT'],
    $errno, $errstr, 0.01))) {
                  fputs($fh,
                      "GET $job_url HTTP/1.0\r\n"
                          . "Host: {$_SERVER['HTTP_HOST']}\r\n\r\n"
                  );
                  fgets($fh,32);
                  fclose($fh);
              }

} else {
   echo "Увы, мир жесток и безжалостен...";
}
</pre>
<p>Что здесь происходит? Мы создали php-файл, при выполнении которого отправится письмо, затем подключились к веб-серверу и запросили скрипт job.php, передав ему параметром имя только что созданного файла. И тут же отключились &#8211; ведь нам не важен результат, мы уже солгали пользователю о том, что операция завершилась успешно. На всё-про всё потратили доли секунды.  Дальше уже дело техники &#8211; job.php захватит lock-файл, проверить наличие файла, имя которого ему передали, выполнит его, удалит в случае успеха, а затем отпустит lock-файл. Конечно надо не забывать, что скрипт может по каким-то причинам не выполнится (почтовый сервер недоступен, или ответит ошибкой, да и мало ли какие напасти происходят в реальности), поэтому следует вызывать job.php ещё и ещё, но пользователя это уже не должно волновать &#8211; у вас его письмо сохранилось и вы его доставите, он вам верит!</p>
<p>Разумеется решение годится не только для отправки почты, но и как я сказал в начале &#8211; <strong>для любых действий, результат которых не нужен немедленно</strong>.</p>
]]></content:encoded>
			<wfw:commentRss>http://miracle.rpz.name/2011/04/16/asynchronous-jobs-with-pure-php/feed/</wfw:commentRss>
		<slash:comments>7</slash:comments>
		</item>
		<item>
		<title>Решения и их последствия.</title>
		<link>http://miracle.rpz.name/2011/04/16/ie9-and-custom-namespaced-attributes/</link>
		<comments>http://miracle.rpz.name/2011/04/16/ie9-and-custom-namespaced-attributes/#comments</comments>
		<pubDate>Sat, 16 Apr 2011 11:28:36 +0000</pubDate>
		<dc:creator>MiRacLe</dc:creator>
				<category><![CDATA[DailyWTF]]></category>
		<category><![CDATA[dev]]></category>
		<category><![CDATA[web]]></category>
		<category><![CDATA[ie]]></category>
		<category><![CDATA[ieupdate]]></category>
		<category><![CDATA[jquery]]></category>

		<guid isPermaLink="false">http://miracle.rpz.name/?p=429</guid>
		<description><![CDATA[Разрабатывать продукт в недра которого может влезть &#8220;чужой&#8221; непросто. Мы придумываем всяческие &#8220;неймспейсы&#8221;, давая файлам, директориям, переменным и классам псевдо-уникальные префиксы, пытаясь предотвратить коллизии с чужеродной средой. Однажды, много лет назад, ко мне свалилась задача &#8211; размещение некой сложной формы на сторонних ресурсах. Очевидное и годами отработанное решение &#8211; iframe было забраковано, т.к. опыт и [...]]]></description>
			<content:encoded><![CDATA[<p>
Разрабатывать продукт в недра которого может влезть &#8220;чужой&#8221; непросто. Мы придумываем всяческие &#8220;неймспейсы&#8221;, давая файлам, директориям, переменным и классам псевдо-уникальные префиксы, пытаясь предотвратить коллизии с чужеродной средой. </p>
<p>
Однажды, много лет назад, ко мне свалилась задача &#8211; размещение некой сложной формы на сторонних ресурсах. Очевидное и годами отработанное решение &#8211; iframe было забраковано, т.к. опыт и <strike>пятая точка</strike>шестое чувство подсказывали &#8211; как только владелец сайта разместит форму у себя, его светлую голову немедленно посетит мысль &#8211; &#8220;а как мне приделать перламутровые пуговицы?!&#8221;. Это, признаться, пугало &#8211; целыми днями верстать и раскрашивать незнакомым дядям и тётям одну и ту же страницу пятью миллионами способов? Нет, пристрелите меня семеро! Второй способ &#8211; разработать и задокументировать API, чтобы владелец ресурса сам разрабатывал себе форму, был отвергнут как трудозатратный, но совершенно нежизнеспособный так как рядовой клиент как правило (тут могло бы быть нечто оскорбительное про уровень интеллекта и радиус кривизны передних конечностей, но цензура не пропустит) не готов своими силами что-то создавать, тогда как продукт конкурента был проще в освоении (работал из коробки). Был выбран третий путь &#8211; размещение формы в виде html-разметки, которую мы отдаём заказчику, что называется &#8220;в руки&#8221;, а данные подгружаем через <em>&lt;script src=&quot;http://my-resource.example.com/?parameters&quot;&gt;&lt;/script&gt;</em>, это позволяло владельцу ресурса до определённой степени контролировать внешний вид полученного документа и избавляло нас от трудозатратной кастомизации сотен инсталляций продукта. Но этот способ потенциально создавал много проблем, т.к. находясь в инородной среде наш документ мог &#8220;подхватить&#8221; незапланированные свойства, &#8220;заразится&#8221; чужими переменными и вообще рассыпаться в пыль в руках неуклюжего веб-мастера. Необходимо было тщательно изолировать всё и вся. Одним из способов отделить своё &#8220;добро&#8221; внутри документа было создание уникального &#8220;пространства имён&#8221;, например создание атрибутов в виде <em>&lt;div rpz:property=&quot;value&quot;&gt;&lt;/div&gt;</em> &#8211; это самое <b>rpz:</b> давало надежду на то, что внешний скрипт(движок сайта или сам вебмастер) &#8220;случайно&#8221; не создаст аналогичный атрибут с другим value (или вовсе без него), развалив всю эту шаткую конструкцию. Решение оказалось рабочим во всех доступных графических браузерах и популяризировалось мной не только в этом продукте, но и повсеместно в других <strike>(печатая эти строки пытаюсь понять &#8211; зачем вообще нужны были именно атрибуты и почему их нельзя было заменить на переменные в скрипте, но разумного ответа почему-то не нахожу)</strike>.</p>
<p>
Это неоднозначное решение &#8220;выстрелило в ногу&#8221; только на днях &#8211; появился IE9&#8230; <strike>Невероятно быстр, ангельски красив, дьявольски умён &#8211; блеск, а не браузер</strike> <strike>На вид такой же унылый как все предыдущие</strike> В общем обзор новшеств можно наверное найти на сайте разработчиков, ну а я увидел его первый раз два дня назад, ничего <strike>хорошего</strike> про него сказать не могу, да и речь совсем не о том. Случись такое совпадение &#8211; сайт заказчика оказался настолько весь из себя валиден, что IE9 работал со страницей в своём новом <strong>document.documentMode</strong>. И чтобы вы думали? Точно! Годами отработанная &#8220;технология&#8221; дала сбой &#8211; &#8220;самодельные&#8221; атрибуты просто-напросто не видны на такой странице, а моя твёрдая уверенность в том, что они есть запретила здравому смыслу проверять их наличие&#8230; так и случаются epic fail-ы, так было и со мной.</p>
<p>
Один грязный хак удалось временно залатать <a href="http://msdn.microsoft.com/ru-ru/library/cc288325%28v=vs.85%29.aspx">другим грязным хаком</a>, но урок получен &#8211; недокументированная возможность это лишь &#8220;возможность&#8221;, строить на ней что-то прочное нельзя.</p>
<p>
К слову сказать, не так давно вместо атрибута rpz:property=&quot;value&quot;, я начал использовать &#8220;<a href="http://dev.w3.org/html5/spec/elements.html#embedding-custom-non-visible-data-with-the-data-attributes">документированную возможность</a>&#8221; &#8211; атрибут data-rpz-property=&quot;value&quot; и соответствующий вызов в js изменился с <a href="http://api.jquery.com/attr/">jQuery(selector).attr</a>(&quot;rpz:property&quot;) на <a href="http://api.jquery.com/data/">jQuery(selector).data</a>(&quot;rpz-property&quot;) , видимо &#8220;шестое чувство&#8221; о чём-то подозревало&#8230;</p>
]]></content:encoded>
			<wfw:commentRss>http://miracle.rpz.name/2011/04/16/ie9-and-custom-namespaced-attributes/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>jabber web status</title>
		<link>http://miracle.rpz.name/2010/11/20/jabber-web-status-2/</link>
		<comments>http://miracle.rpz.name/2010/11/20/jabber-web-status-2/#comments</comments>
		<pubDate>Sat, 20 Nov 2010 17:00:44 +0000</pubDate>
		<dc:creator>MiRacLe</dc:creator>
				<category><![CDATA[dev]]></category>
		<category><![CDATA[js]]></category>
		<category><![CDATA[web]]></category>
		<category><![CDATA[jabber]]></category>
		<category><![CDATA[node]]></category>

		<guid isPermaLink="false">http://miracle.rpz.name/?p=373</guid>
		<description><![CDATA[В поисках прикладной задачи для предметного изучения node.js вспомнил про заброшенный проектик &#8211; jabber web status. И за несколько часов переписал на node. По сравнению с предыдущей версией написанной на PHP (XMPPHP) потребление памяти, а главное, нет ГЛАВНОЕ &#8211; потребление CPU снизилось до статистической погрешности (по непонятным причинам php-бот иногда забирает до 60% процессорного времени) [...]]]></description>
			<content:encoded><![CDATA[<p>В поисках прикладной задачи для предметного изучения <a href="http://nodejs.org">node.js</a> вспомнил про заброшенный проектик &#8211; <a href="http://miracle.rpz.name/2009/12/05/jabber-web-status/">jabber web status</a>.</p>
<p>И за несколько часов переписал на <strong>node</strong>. По сравнению с предыдущей версией написанной на PHP (<a href="http://code.google.com/p/xmpphp/">XMPPHP</a>) потребление памяти, а главное, нет ГЛАВНОЕ &#8211; потребление CPU снизилось до статистической погрешности (по непонятным причинам php-бот иногда забирает до 60% процессорного времени) . Скорость работы &#8211; отдельная, приятная на слух песня. Избавился от промежуточного хранилища (а значит и от лага в обновлении информации), в котором хранились статусы пользователей &#8211; скрипт не только работает с xmpp, но и сам раздаёт результат по http.</p>
<p>Работать с node  ново и свежо. Удручает только то, что свежесть во всём &#8211; в библиотеках, в сборке, установке дополнительных модулей. Написание и отладка скриптика отняли от силы час, правка библиотек ещё два<span style="text-decoration: line-through;">, установка node и модулей на сервере с дебильной Centos &#8211; целую вечность</span>.  Но всё-таки оно того стоит &#8211; писать на javascript легко и приятно, очень интересно использовать его вне привычных рамок браузера, асинхронность везде и во всём, интерпретатор, шустрый как электровеник, человеко-понятно ругается ошибками, неплохая документация к основным модулям. В общем хороший массаж не только для коры головного мозга, но и самой его древесины, изрядно дубеющих от PHP ;o)</p>
<p>&nbsp;</p>
]]></content:encoded>
			<wfw:commentRss>http://miracle.rpz.name/2010/11/20/jabber-web-status-2/feed/</wfw:commentRss>
		<slash:comments>6</slash:comments>
		</item>
		<item>
		<title>jabber web-status</title>
		<link>http://miracle.rpz.name/2009/12/05/jabber-web-status/</link>
		<comments>http://miracle.rpz.name/2009/12/05/jabber-web-status/#comments</comments>
		<pubDate>Sat, 05 Dec 2009 18:35:53 +0000</pubDate>
		<dc:creator>MiRacLe</dc:creator>
				<category><![CDATA[advertise]]></category>
		<category><![CDATA[web]]></category>
		<category><![CDATA[jabber]]></category>

		<guid isPermaLink="false">http://miracle.rpz.name/?p=301</guid>
		<description><![CDATA[В процессе работы над Секретным Проектом™ образовался побочный продукт, который возможно покажется кому-то полезным. Суть &#171;продукта&#187; &#8211; показать статус вашего jabber-аккаунта в вебе, как это делается например для icq. Для работы требуется добавить себе в ростер контакт webstatus@rpz.name, после этого ваш статус будет доступен по следующим ссылкам: http://webstatus.rpz.name/your@jabber.account &#8211; в виде html http://webstatus.rpz.name/your@jabber.account/image &#8211; картинки [...]]]></description>
			<content:encoded><![CDATA[<p>В процессе работы над Секретным Проектом™ образовался побочный продукт, который возможно покажется кому-то полезным. Суть &laquo;продукта&raquo; &#8211; показать статус вашего jabber-аккаунта в вебе, как это делается например для <a href="http://www.icq.com/features/web/indicator.html">icq</a>.</p>
<p>Для работы требуется добавить себе в ростер контакт <strong>webstatus@rpz.name</strong>, после этого ваш статус будет доступен по следующим ссылкам:</p>
<blockquote><ul>
<li><a href="http://webstatus.rpz.name/your@jabber.account">http://webstatus.rpz.name/your@jabber.account</a> &#8211; в виде html</li>
<li><a href="http://webstatus.rpz.name/your@jabber.account/image">http://webstatus.rpz.name/your@jabber.account/image</a> &#8211; картинки 16&#215;16 (взяты из стандартного набора <a href="http://psi-im.org/">psi</a>)
</li>
<li><a href="http://webstatus.rpz.name/your@jabber.account/script">http://webstatus.rpz.name/your@jabber.account/script</a> &#8211; для тех, кто может себе позволить &lt;script src=&quot;http://webstatus.rpz.name/your@jabber.account/script&quot;&gt;&lt;/script&gt;</li>
<li><a href="http://webstatus.rpz.name/your@jabber.account/json">http://webstatus.rpz.name/your@jabber.account/json</a></li>
<li><a href="http://webstatus.rpz.name/your@jabber.account/json?callback=yourfunction">http://webstatus.rpz.name/your@jabber.account/json?callback=yourfunction</a></li>
<li><a href="http://webstatus.rpz.name/your@jabber.account/xml">http://webstatus.rpz.name/your@jabber.account/xml</a></li>
</ul>
</blockquote>
<p>Сделано <strike>как обычно</strike> just for fun. Вы можете пользоваться этим сервисом как заблагорассудится, можете писать пожелания и комментарии, но не можете предъявлять претензии&#8230;</p>
]]></content:encoded>
			<wfw:commentRss>http://miracle.rpz.name/2009/12/05/jabber-web-status/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>IE8 и currentStyle</title>
		<link>http://miracle.rpz.name/2009/03/20/currentstyle-ie8/</link>
		<comments>http://miracle.rpz.name/2009/03/20/currentstyle-ie8/#comments</comments>
		<pubDate>Fri, 20 Mar 2009 15:38:58 +0000</pubDate>
		<dc:creator>MiRacLe</dc:creator>
				<category><![CDATA[DailyWTF]]></category>
		<category><![CDATA[dev]]></category>
		<category><![CDATA[web]]></category>
		<category><![CDATA[css]]></category>
		<category><![CDATA[ie]]></category>
		<category><![CDATA[ie8]]></category>

		<guid isPermaLink="false">http://miracle.rpz.name/?p=217</guid>
		<description><![CDATA[Новость одной строкой&#8230; Когда-то я рассказал как добавить border-spacing для IE используя свойство currentStyle. Вчера, как многие знают, вышел IE8.Собственно на этом можно было бы многозначительно пост закончить&#8230; В общем в IE8 более этот трюк не работает. В currentStyle не попадают неизвестные браузеру свойства. Изящного решения пока не нашёл &#8211; если уж пользовались им, то [...]]]></description>
			<content:encoded><![CDATA[<p>Новость одной строкой&#8230; Когда-то я рассказал как добавить <a href="http://miracle.rpz.name/2007/12/13/border-spacing-for-ie/">border-spacing для IE</a> используя свойство <strong>currentStyle</strong>.</p>
<p>Вчера, как многие знают,  вышел <a href="http://microsoft.com/ie8">IE8</a>.Собственно на этом можно было бы многозначительно пост закончить&#8230; </p>
<p>В общем в IE8 более этот трюк не работает. В currentStyle не попадают неизвестные браузеру свойства. Изящного решения пока не нашёл &#8211; если уж пользовались им, то просто замените непонятные буквы в css на <b>cellSpacing = &lt;число&gt;</b> вместо вычислений, которые были раньше&#8230;</p>
<p><strong>Update</strong> а вот <a href="http://designformasters.info/posts/sovmestimost-saytov-s-ie8/">переводная статья о других особенностях IE8</a></p>
]]></content:encoded>
			<wfw:commentRss>http://miracle.rpz.name/2009/03/20/currentstyle-ie8/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>AJAX и проблемы с кодировкой</title>
		<link>http://miracle.rpz.name/2008/11/28/ajax-charset/</link>
		<comments>http://miracle.rpz.name/2008/11/28/ajax-charset/#comments</comments>
		<pubDate>Fri, 28 Nov 2008 10:50:30 +0000</pubDate>
		<dc:creator>MiRacLe</dc:creator>
				<category><![CDATA[dev]]></category>
		<category><![CDATA[web]]></category>
		<category><![CDATA[ajax]]></category>
		<category><![CDATA[charset]]></category>

		<guid isPermaLink="false">http://miracle.rpz.name/?p=162</guid>
		<description><![CDATA[Вот уже полтора года в draft-ах пылился пост о надуманности проблем с кодировками и т.н. AJAX-ом. Каждый раз, когда на форумах всплывали вопросы подобного характера, хотелось дать ссылку, на всякий всплеск заходов на блог по запросам &#8220;кодировка, ajax, проблема&#8221; хотелось его опубликовать, но мне казалось, что пост ещё не закончен, надо ещё чуть-чуть дописать&#8230; Но [...]]]></description>
			<content:encoded><![CDATA[<p>Вот уже полтора года в draft-ах пылился пост о надуманности проблем с кодировками и т.н. AJAX-ом.<br />
Каждый раз, когда на форумах всплывали вопросы подобного характера, хотелось дать ссылку, на всякий всплеск заходов на блог по запросам &#8220;кодировка, ajax, проблема&#8221; хотелось его опубликовать, но мне казалось, что пост ещё не закончен, надо ещё чуть-чуть дописать&#8230;<br />
Но вот буквально сегодня появился удивительно похожий пост &#8211; <a href="http://blog.fxposter.org/2008/11/28/jquery-ajax-and-cp1251/">ajax, cp1251</a>. Похожий по содержанию, но совершенно противоположный по смыслу.<br />
Посему свой черновичок я решил удалить, а поведать свою &#8220;истину&#8221; в форме критики совета fxposter-а.</p>
<blockquote><p>Ни для кого ни секрет, что кодировкой получаемых через Ajax данных по-умолчанию принимается UTF-8.</p></blockquote>
<p>На самом деле это секрет. Для многих секрет. И многие не понимают почему это так.<br />
Внутреннее представление строк (и регулярных выражений) в JavaScript для всех не-ASCII последовательностей как раз UTF-8.<br />
Отсюда и проистекает т.н. &#8220;проблема&#8221; &#8211; если кодировка не указана явно и используется нелатиница, она будет интерпретирована как utf-8 последовательность.</p>
<blockquote><p><b>Update 29.11</b> Свежий воздух и Давид Мзареулян остудили пыл, поэтому спешу уточнить о чём именно будет идти речь ниже.<br />
Итак &#8211; у вас есть некий ресурс в однобайтовой кодировке (к гадалке не ходи это будет windows-1251) и вы озаботились освоить новый buzzword по имени AJAX. Немного почитав, вы делаете первые робкие шаги в этом направлении и тут же наступаете на &#8220;детские грабли&#8221;, а затем, немного отдышавшись, мчитесь на форумы с криком о помощи. И вам эту помощь окажут &#8211; переделай мол, свой ресурс на utf-8&#8230; Конечно-конечно скажете вы и пойдёте переделывать&#8230;<br />
Я же хочу остеречь от таких опрометчивых шагов.
</p></blockquote>
<p>Cтандартное решение, которое наперебой советуют все &#8211; &#8220;используй utf-8 и нет проблем&#8221;.</p>
<p>И советчики правы &#8211; проблем действительно не будет.</p>
<p>Просто трафик увеличится &#8220;вдвое&#8221;. Те же данные, тот же результат, а трафика &#8220;в два раза&#8221; больше. Ага?</p>
<p><span style="text-decoration: line-through;">Что вы там говорите насчёт порошка?!?</span></p>
<p>Если вам этот фактор кажется мало***щим, то на этом чтение надо прекратить и начать переделывать свой проект на использование UTF-X,<br />
остальным же оставлю несколько рецептов, которые помогут избежать проблем при использовании однобайтовых кодировок в т.н. AJAX-приложениях: <span id="more-162"></span></p>
<ul>
<li>Первое, оно же главное &#8211; ВСЕГДА указывайте кодировку контента. В любом ответе сервера с текстовым контентом обязан быть заголовок <i>Content-Type: your/type; charset=your-charset</i>.<br />
Дешевле всего это сделать, настроив сервер (например в php через <a href="http://ru2.php.net/manual/en/ini.core.php#ini.default-charset">default_charset</a>)</li>
<li>Указывайте charset при включении javascript в тело документа (&lt;script type=&quot;text/javascript&quot; charset=&quot;your-charset&quot;&gt; )</li>
<li>Указывайте ПРАВИЛЬНЫЙ charset<br />
<blockquote><p>предварительно установив соответствующий заголовок &#8211; &#8220;Content-Type: text/html; charset=cp1251&#8243;</p></blockquote>
<p>В данном <span style="text-decoration: line-through;">конкретном</span> <span style="text-decoration: line-through;">взятом за жопу</span> случае fxposter сам себе злобный буратина.</p>
<blockquote><p>Any registered <a href="http://www.iana.org/assignments/character-sets">IANA charset</a> may be used, but UTF-8 is preferred.</p></blockquote>
<p>Ну нету среди any registered кодировки с названием cp1251&#8230;</li>
</ul>
<p>Для полноты картины приведу пару проблемных моментов, с которыми столкнуться прийдётся:</p>
<ul>
<li>Не позволяйте AJAX-ответам, которые содержат &#8220;нелатиницу&#8221; оставаться в кеше браузера (при 304 Not Modified ответ поднимется из кеша, но в качестве charset &#8220;некоторые браузеры&#8221; используют utf-8)</li>
<li>
<blockquote><p><a href="http://www.ietf.org/rfc/rfc4627.txt">JSON text SHALL be encoded in Unicode</a>.  The default encoding is UTF-8.</p></blockquote>
<p>Этим правилом НАГЛО пользуются производители различных библиотек для json_[en|de]code, но браузерам (как мы выяснили ранее) главное кодировку указать, а там всё разрулится.<br />
Отсюда и &#8220;проблема&#8221; &#8211; кодировать данные в JSON нужно вручную, распространнёные библиотечные функции на входе ожидают utf-8.</li>
</ul>
<p>Мораль сей басни я жду от вас в комментариях.</p>
]]></content:encoded>
			<wfw:commentRss>http://miracle.rpz.name/2008/11/28/ajax-charset/feed/</wfw:commentRss>
		<slash:comments>16</slash:comments>
		</item>
		<item>
		<title>Cross Domain XMLHttpRequest</title>
		<link>http://miracle.rpz.name/2008/10/03/crossdomain-xhr-with-flash/</link>
		<comments>http://miracle.rpz.name/2008/10/03/crossdomain-xhr-with-flash/#comments</comments>
		<pubDate>Thu, 02 Oct 2008 21:50:52 +0000</pubDate>
		<dc:creator>MiRacLe</dc:creator>
				<category><![CDATA[dev]]></category>
		<category><![CDATA[js]]></category>
		<category><![CDATA[web]]></category>
		<category><![CDATA[ajax]]></category>
		<category><![CDATA[flash]]></category>

		<guid isPermaLink="false">http://miracle.rpz.name/?p=116</guid>
		<description><![CDATA[Задачи обмена информацией ставятся и успешно решаются каждый день. Но обмениваться можно по-разному. Кто-то дарит удобоваримый доступ к своей базе посредством распространённых обменных форматов (xml, csv, json, lisiy_chert), кто-то реализует собственные API, а кто-то идёт другими путями. Моя задача состояла в следующем &#8211; на ресурсах-сателитах необходимо разместить сложную форму. &#8220;Сложность&#8221; формы заключается в том, что [...]]]></description>
			<content:encoded><![CDATA[<p>Задачи обмена информацией ставятся и успешно решаются каждый день. Но обмениваться можно по-разному. Кто-то дарит удобоваримый доступ к своей базе посредством распространённых обменных форматов (xml, csv, json, lisiy_chert), кто-то реализует собственные API, а кто-то идёт другими путями.</p>
<p>Моя задача состояла в следующем &#8211; на ресурсах-сателитах необходимо разместить сложную форму. &#8220;Сложность&#8221; формы заключается в том, что данные подгружаются с главного ресурса и не могут быть загружены единовременно(при загрузке ресурса) или доставлены на ресурс-сателит заранее (так-так данных очень много и они достаточно быстро устаревают). Всевозможные API для доступа к информации основного ресурса в настоящий момент разрабатывать нецелесообразно, поэтому было решено для сателитов предоставлять некий готовый комплекс (аля plug-n-play).</p>
<blockquote><p>
Ещё до начала разработки я тщательно изучил уже имеющиеся механизмы для межсайтового обмена данными. Первым и самым перспективным был вариант использования flash-плеера, но единственный вменяемый пример <a href="http://blog.monstuff.com/archives/000294.html">FlashXmlHttpRequest</a> был только лишь примером, а не законченным куском кода, которым бы можно было воспользоваться. <a href="http://jquery.com/">jQuery</a> на тот момент даже не содержала функции $.getScript,  <a href="http://dklab.ru/lib/JsHttpRequest/">JSHttpRequest</a> с созданием тега script удачно справлялся, но POST по понятным причинам делать не мог.
</p></blockquote>
<p>Данные с главного ресурса могут подгружатся  посредством динамического создания тега script <a href="http://docs.jquery.com/Ajax/jQuery.getScript">jQuery.getScript</a>, с этим казалось бы проблемы нет. Но! Но последним шагом в указанной форме нужно отправить на основной сервер внушительный объём данных, которые могут не влезть в GET (тоже кстати говоря весьма интересный вопрос &#8211; а каково ограничение на длину URL в разных браузерах? в различных веб-серверах,прокси и фильтрах? &#8211; в <a href="http://www.ietf.org/rfc/rfc2616.txt">RFC2616</a> об этом не сказано). Можно конечно изобрести какие-либо механизмы, например отправлять данные небольшими порциями GET-ом, но скорости такая схема явно не прибавит, поэтому такие варианты оставлены другим изобретателям.<br />
<span id="more-116"></span></p>
<p>В первом рабочем варианте проблема решалась php-скриптом на сервере-сателите, через который данные POST-ом прокачивались на головной сайт. Но такая схема имела ряд недостатков, основным из которых опять же является скорость работы и дополнительное требование к ПО на сервере (наличие PHP) или требование к нам, разработчикам &#8211; создать прокси-скрипт для других распространнёных серверных платформ. </p>
<p><strike>И вот относительно недавно</strike> я наткнулся на занимательную библиотеку <a href="http://flxhr.flensed.com/">flxhr</a>. Автор довёл до конца идею и наваял интерфейс, который может <b>прозрачно заменять XMLHttpRequest</b> используя flash. Использовать flxhr легко и просто &#8211; <a href="http://flxhr.flensed.com/code/tests/flxhr-7f.html">заменяете стандартный XMLHTTPRequest</a> и дальше всё почти как обычно. </p>
<p>В дополнение ко всему, флеш-плеер решает ещё одно требование к разрабатываемому проекту &#8211; необходимо пресечь бесконтрольное распространение клиентского модуля(который устанавливается на сайт-сателит). Дело в том что в флеш-плеер от рождения встроен механизм контроля над внешними данными. Ранее это был ресурс crossdomain.xml в корне веб-сервера, содержащего скачиваемые данные. В нём описывалось откуда и что можно скачать. В новом плеере (а для использования flxhr необходим плеер версии не ниже 9.0.0.124) появился более удобный механизм &#8211; можно указать путь к ресурсу с политиками <a href="http://www.adobe.com/devnet/flashplayer/articles/fplayer9_security.html#goal_control">policyURL</a>.</p>
<blockquote><p>
 Кстати о версии плеера:  автор flxhr предусмотрел не только проверку версии плеера, но и возможность его апгрейдить не отходя от кассы (тот самый <a href="http://blog.deconcept.com/swfobject/#expressinstall">expressinstall из swfobject</a>).
</p></blockquote>
<p> Думаю стоит ещё раз упомянуть тот факт, что данная схема <b>не позволит</b> обратиться js-скрипту с произвольного сайта A на произвольный ресурс B без согласия его владельца. &#8220;Согласие&#8221;  выражается в виде правил, описываемых в файле crossdomain.xml(или указанным в loadPolicyFile), который располагается на сервере B. Другими словами данные, которые вы желаете публиковать, не должны быть &#8220;ворованными&#8221;.</p>
<p> Единственным неудобством, с которым пока довелось столкнуться в ходе обкатки flxhr, является тот факт, что данные в кодировке отличной от UTF-8 безвозвратно портятся. В остальном это самый <b>лёгкий и удобный</b> способ обмениваться данными между разными доменами уже <b>сейчас</b>, не дожидаясь появления сверхновых версий браузеров, в которых реализуют <a href="http://dev.w3.org/2006/webapi/XMLHttpRequest-2/">XMLHttpRequest Level 2</a>, и скоропостижной смерти всех старых.</p>
]]></content:encoded>
			<wfw:commentRss>http://miracle.rpz.name/2008/10/03/crossdomain-xhr-with-flash/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>ты не один</title>
		<link>http://miracle.rpz.name/2008/09/23/remember-you-are-not-alone/</link>
		<comments>http://miracle.rpz.name/2008/09/23/remember-you-are-not-alone/#comments</comments>
		<pubDate>Tue, 23 Sep 2008 19:35:43 +0000</pubDate>
		<dc:creator>MiRacLe</dc:creator>
				<category><![CDATA[DailyWTF]]></category>
		<category><![CDATA[dev]]></category>
		<category><![CDATA[php]]></category>
		<category><![CDATA[web]]></category>
		<category><![CDATA[tips]]></category>

		<guid isPermaLink="false">http://miracle.rpz.name/?p=115</guid>
		<description><![CDATA[Приключилась сегодня очень познавательная история, мораль которой должен понимать любой разработчик, детище которого обслуживает более одного пользователя. В рамках работ по выводу одного &#8220;неповоротливого асфальтоукладчика&#8221; на земную орбиту были проведены ряд его модернизаций. Испытания на тестовом полигоне показали что машина стала ездить на порядок быстрее и было решено доработки включить в работающий прототип(вроде и не [...]]]></description>
			<content:encoded><![CDATA[<p>Приключилась сегодня очень познавательная история, мораль которой должен понимать любой разработчик, детище которого обслуживает более одного пользователя.</p>
<p>В рамках работ по выводу одного &#8220;неповоротливого асфальтоукладчика&#8221; на земную орбиту были проведены ряд его модернизаций. Испытания на тестовом полигоне показали что машина стала ездить на порядок быстрее и было решено доработки включить в работающий прототип(вроде и не первая космическая, но уже есть чем гордиться!). Но на первом же серьёзном показе зверь-машина с чудо{вищным} грохотом рассыпалась у всех на глазах и я сел в лужу&#8230;</p>
<p>А теперь тоже самое, но по-русски, с выражением.<br />
Имеются ряд весьма тяжёлых для веба запросов к БД, результат которых условно не меняется в течении небольшого промежутка времени. Кеширование промежуточных результатов в самой БД нагрузку сняло на СУБД, но скрипты продолжали исправно тягать данные и это порой занимало внушительное время. Было решено результаты эти кешировать на стороне веб-сервера. Как хранилище был использован <a href="http://php.net/apc">apc</a>.</p>
<p>Первоначальный вариант выглядел примерно так:</p>
<pre class="php:nocontrols">
if (!$data = cache_get($key)) {
   $data = data_from_db();
   cache_set($key,$data,$expire_time)
}
</pre>
<p>Без излишеств, просто и со вкусом. Но позже выснилось что &#8220;всё не так просто&#8221; и в зависимости от некоторых космических характеристик и результатов выборки из базы будет меняется ещё одна переменная (меняется переменная&#8230; ого!).</p>
<pre class="php:nocontrols">
if (!$data = cache_get($key)) {
   $data = data_from_db();
   cache_set($key,$data,$expire_time);
   $other_data = some_function($data,$env);
   cache_set($other_key,$other_data,$expire_time);
} else {
   $other_data = cache_get($other_key);
}
</pre>
<p>Аляповато, но, чёрт возьми, работает&#8230;<br />
Работало&#8230;<br />
Должно было по идее работать&#8230;<br />
Но, почему-то оказавшись на боевом, изрядно нагруженном, сервере это не сработало. Вернее это работало&#8230;,  но не всё время&#8230; По истечении $expire_time периодически пропадали данные для $other_key. При отключении и/или очистке кеша работоспособность восстанавливалась&#8230; Кто виноват? Что делать? Кто за всё это ответит?!? Пришлось срочно пошевелить опилками.</p>
<p>Как мы все наверное догадываемся &#8211; реальный сервер обслуживает нереально много клиентов, причём не по очереди. И нет совершенно никаких гарантий на то, что между записью $key и $other_key не встрянет какой-либо маленький, но шустрый процесс (хотя нам это и не особо страшно), так же нет никаких оснований полагать что между выборкой $key и $other_key кто-то умный и большой не очистит кеш (целиком, или только $other_key &#8211; в данном эпизоде это не имеет значения).<br />
Оснований не было, но я был почему-то твёрдо уверен, что &#8220;это если и происходит, то с кем-то другим&#8221;. С пониманием проблемы конечно же пришло и простое решение, но ошибка, согласитесь, совсем не тривиальная.</p>
<p>Так что мораль сей басни: помни &#8211; ты не один!</p>
]]></content:encoded>
			<wfw:commentRss>http://miracle.rpz.name/2008/09/23/remember-you-are-not-alone/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>firebug для отладки серверного кода</title>
		<link>http://miracle.rpz.name/2008/05/06/firebug-console-for-server-side-messages/</link>
		<comments>http://miracle.rpz.name/2008/05/06/firebug-console-for-server-side-messages/#comments</comments>
		<pubDate>Tue, 06 May 2008 10:41:31 +0000</pubDate>
		<dc:creator>MiRacLe</dc:creator>
				<category><![CDATA[dev]]></category>
		<category><![CDATA[js]]></category>
		<category><![CDATA[php]]></category>
		<category><![CDATA[web]]></category>
		<category><![CDATA[debug]]></category>
		<category><![CDATA[firebug]]></category>

		<guid isPermaLink="false">http://miracle.rpz.name/?p=107</guid>
		<description><![CDATA[За последние пару лет firebug стал стандартным инструментом для отладки клиентской части веб-приложений у большинства разработчиков. Тем, у кого это не так &#8211; от души сочувствую ;o) Но многие не догадываются, что с помощью firebug можно отлаживать и серверный код. Не breakpoint-ами конечно. Но как замена унылым print-ам и var_dump-ам очень даже! Собственно делается всё [...]]]></description>
			<content:encoded><![CDATA[<p><a rel="tn" href="http://miracle.rpz.name/shared/fb/firebug.png"><img class="tn" src="http://miracle.rpz.name/shared/fb/tn_firebug.png" alt="" align="right" /></a><br />
За последние пару лет <a href="http://getfirebug.com">firebug</a> стал стандартным инструментом для отладки клиентской части веб-приложений у большинства разработчиков.<br />
Тем, у кого это не так &#8211; от души сочувствую ;o)<br />
Но многие не догадываются, что с помощью firebug можно отлаживать и серверный код. Не breakpoint-ами конечно. Но как замена унылым print-ам и var_dump-ам очень даже!</p>
<p>Собственно делается всё очень просто. Для того чтобы в консоли firebug-а появилось сообщение, необходимо написать js-код:</p>
<pre class="js:nocontrols">
console.log('сообщение');
console.group('разные типы сообщений');
console.info('к вашему сведению, мы тут логи пишем...');
console.warn('предупреждаем о разном');
console.error('и информируем об ошибках!!!');
console.groupEnd();
console.dir({сложная: {структура: [1,2,3]}});</pre>
<p>Подробнее о возможностях консоли firebug-а можно узнать в <a href="http://getfirebug.com/console.html">документации</a>.</p>
<p>Дело за малым &#8211; сформировать эти строки на вашей серверной платформе.<br />
Сложность представляет лишь последнее &#8211; <a href="http://json.org/">json</a>-упаковка сложных структур, но и она уже во многих платформах решена. Сам я пользуюсь функцией, описанной в коментариях к <a href="http://php.net/json_encode">json_encode</a>, т.к. родная слишком консервативна в вопросах кодировок(<a title="JSON text SHALL be encoded in Unicode.  The default encoding is UTF-8." href="http://www.ietf.org/rfc/rfc4627.txt?number=4627">по стандарту в json можно упаковать только уникод</a>, но браузеры более лояльны нежели писатели стандартов).</p>
<p>Собственно теперь чем это лучше всяких принтов и вар_дампов?<br />
Тем, что не меняют визуально документ, что существенно в современных сложных веб-приложениях.<br />
К тому же это в разы удобнее &#8211; все сообщения в одном месте, красиво представлены&#8230;</p>
<p>В заключение скажу о якобы готовых решениях для PHP:<br />
<a href="http://www.indelible.org/php/Log/guide.html#the-firebug-handler">Log::Firebug</a> из <a href="http://pear.php.net">PEAR</a> &#8211; драйвер для довольно распространнённого pear::log<br />
<a href="http://firephp.org/">FirePHP</a> &#8211; более комплексное решение, плагин к firebug-у и обёртка к серверному коду.</p>
<p>Я ни тем, ни другим не пользуюсь, т.к. первое сделано левой ногой, а второе заворачивает оригинальный документ в страшную multipart-кашу&#8230;</p>
]]></content:encoded>
			<wfw:commentRss>http://miracle.rpz.name/2008/05/06/firebug-console-for-server-side-messages/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Всплытие покажет</title>
		<link>http://miracle.rpz.name/2008/05/05/event-bubbling/</link>
		<comments>http://miracle.rpz.name/2008/05/05/event-bubbling/#comments</comments>
		<pubDate>Mon, 05 May 2008 09:51:14 +0000</pubDate>
		<dc:creator>MiRacLe</dc:creator>
				<category><![CDATA[dev]]></category>
		<category><![CDATA[js]]></category>
		<category><![CDATA[web]]></category>
		<category><![CDATA[events]]></category>
		<category><![CDATA[jquery]]></category>

		<guid isPermaLink="false">http://miracle.rpz.name/2008/05/05/event-bubbling/</guid>
		<description><![CDATA[Предположим у нас есть html-таблица 100&#215;100 ячеек,в каждой ячейке &#8211; ссылка. При нажатии на каждую, должно происходить что-либо невероятное. Мы можем сделать 10 тыс обработчиков onclick и медленно, но верно добиться своей цели. А можем глупостей не делать. Ведь хватит только одного! Дело в том, что события в DOM умею &#8220;всплывать&#8221; &#8211; т.е. если представить [...]]]></description>
			<content:encoded><![CDATA[<p>
Предположим у нас есть html-таблица 100&#215;100 ячеек,в каждой ячейке &#8211; ссылка. При нажатии на каждую, должно происходить что-либо невероятное.<br />
Мы можем сделать 10 тыс обработчиков onclick и медленно, но верно добиться своей цели.<br />
А можем глупостей не делать. Ведь хватит только одного!
</p>
<p><img src="http://miracle.rpz.name/shared/event_bubbling.gif" align="right"><br />
 Дело в том, что события в DOM умею &#8220;всплывать&#8221; &#8211; т.е. если представить документ в виде слоёной структуры, где сам document находиться сверху, радуя глаз хрустящей корочкой, а все дочерние элементы под ним, то событие возбуждённое на нижних уровнях, не найдя свой законный обработчик у потревоженного объекта начинает потихоньку лезть наверх, шевеля всё на своём пути, и искать свой обработчик последовательно у всех предков.<br />
Что это собственно даёт? Это может существенно сократить количество обработчиков событий на странице. На идеальной сферической странице вакуумного веб-приложения всего один обработчик!
</p>
<p>Перейду к примерам:<span id="more-106"></span></p>
<pre class="js:nocontrols">
  window.onload = function(){
  var tbl = document.getElementById('tbl'), links = tbl.getElementsByTagName('a'), linkcount = links.length;
     for (var i = 0; i < linkcount; i++){
                  (function(){
                    var num = i;
                     x[i].onclick  = function(){
                     alert('Это обработчик номер ' + num + ' из ' + linkcount + ', ужас правда?');
                     return false;
                  };
        })();
     }
  }
</pre>
<p>Готово. 10000 обработчиков. Легко. Просто. И долго…</p>
<p>А теперь Горбатый! (Я сказал "Горбатый?!?")</p>
<pre class="js:nocontrols">
      window.onload = function(){
         var tbl = document.getElementById("tbl");
         tbl.onclick = function(e) {
                e = e || window.event;
                var elem = e.target || e.srcElement;
                    if ('a' === elem.nodeName.toLowerCase()) {
                            alert('Это обработчик. Один. ЕДИНСТВЕННЫЙ!');
                    }
             }
      }
</pre>
<p>А если не видно разницы, то зачем платить <strike>больше</strike> ?</p>
<p>С jQuery эта техника будет выглядеть следующим образом:</p>
<pre class="js:nocontrols">
    $('#tbl').bind('click',function(e) {
            if ($(e.target).is('a')) {
                    alert('Это обработчик. Один. ЕДИНСТВЕННЫЙ!');
                    return false;
            }
    });
</pre>
<p>В заключение пару плагинов для <a href="http://jquery.com">jQuery</a>, берущие "пыльную" работу в свои руки: </p>
<p><a href="http://flesler.blogspot.com/2007/10/jquerylisten.html">jQuery.listen</a> </p>
<pre class="js:nocontrols">
$('#tbl').listen('click', 'a', function (e) {
    alert('Это обработчик. Один. ЕДИНСТВЕННЫЙ!');
    e.preventDefault();
});
</pre>
<p>и <a href="http://dev.distilldesign.com/log/2008/jan/27/event-delegation-jquery/">jquery.eventdelegation</a></p>
<pre class="js:nocontrols">
$('#tbl').delegate('click', 'a', function () {
    alert('Это обработчик. Один. ЕДИНСТВЕННЫЙ!');
    return false;
});
</pre>
<p>Удачного всплытия!</p>
]]></content:encoded>
			<wfw:commentRss>http://miracle.rpz.name/2008/05/05/event-bubbling/feed/</wfw:commentRss>
		<slash:comments>5</slash:comments>
		</item>
	</channel>
</rss>

<!-- Served from: miracle.rpz.name @ 2012-02-06 18:16:58 by W3 Total Cache -->
