Ничего страшного, если современный сайт не работает с отключенным javascript. Гораздо хуже, если сайт перестаёт работать с включённым javascript.

javascript и php: синхронизация сущностей

Вот форма на сайте ("вот дом, который построил Джек"). Вот кнопка submit, при щелчке по которой запускается функция валидации формы. Вроде всё, как всегда, стандартный пример. Но, если подумать, вопросов вагон.

Кто и в какой момент связал кнопку с функцией? Если это был php-скрипт (в момент генерации html-страницы), то как он узнал о том, что по onclick на кнопку надо навесить функцию validate(this.form)? Некоторые скажут, что это хорошая практика – проверять форму перед отправкой. То есть такой обычай, типа шаблон проектирования. Ладно. Но как, чёрт возьми, php узнал название javascript-функции? Вдруг там, на js, такой функции вовсе и нет? Тогда ведь на странице у пользователя выскочат ошибки.

Ответов в рамках хорошей практики может быть примерно три. Вариант первый – php ничего не знает о javascript. Точнее, не хочет знать, но приходится. В этом случае таких «знаний», связывающих клиентские и серверные скрипты должно быть минимум; их можно назвать «договорённостями». Хороший пример такого рода – минимальный скрипт сортировки html-таблиц simple table sorter: для его корректной работы от php (серверной части генерации html) требуется только одно – указать classname сортируемой html-таблицы ("class='sortable'"); на основании одной этой метки javascript подготовит таблицу к сортировке и позволит сортировать по кликам на заголовках столбцов.

Все «вторые» ответы предполагают очень «плотное» управление javascript'ом со стороны сервера. «Плотное» вовсе не означает, что php должен указывать javascript, как называть функции. Эти варианты предполагают использование шаблона проектирования js-словари: php передаёт javascript максимально подробную информацию о структуре используемых mysql-таблиц и о необычных элементах формы (с которыми должны быть связаны какие-то события). Javascript проверяет все элементы каждой формы (формы – это сверхважно!) по словарю, а в словаре могут быть указания, намёки, что с найденным элементом делать. В случае с кнопкой типа submit никаких особых указаний не нужно (вариантов тут нет), javascript (исходя из хорошей практики) знает даже и без php, что валидацию формы надо проводить всегда.

Это, в частности, означает, что клиенту (браузеру) кнопка submit приходит в «чистом» виде, без всяких onclick. А javascript, после загрузки страницы, сам навешивает на кнопку событие, связывающее её с какой надо функцией:

А где же здесь тогда управление со стороны сервера? – А оно вот в чём: функция validate ведь не знает особенностей полей формы (что проверять). Например, некоторые поля просто не должны быть пустыми. А в некоторых других должно быть выбрано значение из списка. А в некоторых radio-button... ну, в общем, вы поняли. Да, а самое плохое – извлечение русских наименований полей формы: эти наименования ведь могут быть и в соседней ячейке таблицы, и в элементе label или даже P, не обязательно являющегося родителем для искомого input'а. Всё это легко решается с помощью словаря (массива fields):

Пример третьего варианта можно найти на странице allo-irkutsk.ru/klients/price.asp (будет выскакивать окно авторизации, не обращайте внимания – просто убирайте его escape'ом). Там на каждой странице, вверху, есть список флажков:

price_ijs.gif

("Мои", "Удалённые", "Цв. схема"...). Эти флажки не выгружаются в виде массива из php. Изначально они создаются в файле allo-irkutsk.ru/klients/setup.js, и потом их значения передаются серверу через куки. Но этого оказалось недостаточно (все значения из экономии передаются одним числом), надо было передать серверу ещё и наименования параметров. Поэтому файл setup.js здесь используется в качестве ini-файла (php разбирает его и создаёт массив настроек). То есть тут мы имеем нечто прямо противоположное второму варианту: не php управляет javascript, а javascript управляет php (так, оказыватся, тоже бывает).

Обеспечивай связь, или разрывай её полностью

Нет смысла пытаться определить что правильнее, кто изначально должен управлять настройками: php или javascript. Это не имеет значения. Значение имеет логика передачи информации с одной стороны на другую: если один командует, второй подчиняется; а для правильного подчинения ему нужна как можно более полная информация. В этом процессе нужно сводить к минимуму количество неявных договорённостей. Например, указание имени функции javascript (пример в начале статьи) при формировании html-страницы на сервере очень сильно связывает между собой коды клиентских и серверных скриптов.

Дело тут, конечно, не именно в php. В более простом, обнажённом виде проблема обнаруживается, например, в визуальном редакторе. В его кнопках. Их много (Жирный, Курсив, Заголовок...) и при нажатии на каждую должна срабатывать уникальная функция (или функция с уникальными параметрами) javascript. Можно определять эту уникальность, например, с помощью атрибута кнопок id. Кнопки могут лежать в обычном html-файле, подключаемом к странице, например, через iframe. И тут возникает большая дыра, разрыв – куча неявных договорённостей: откуда javascript узнаёт, что при нажатии на кнопку «Жирный» надо запускать функцию execCommand('bold', null, "")? Ну, тут как раз случай достаточно простой. Мы можем информацию о функции записать в classname (class='execCommand'), а первый параметр – в id (id='bold') кнопки, и никому ни о чём не надо будет помнить (после обхода кнопок в цикле и назначения им onclik).

Но как раз в таком стиле («Жирный» -> <b>) визуальный редактор использовать и не надо. А команды strong в API браузеров, интерпретирующих execCommand, не существует (как и большинства других команд для семантической вёрстки). Поэтому мы неизбежно приходим к произвольным командам, которые нужно привязывать к кнопкам. В этом случае, чтобы ликвидировать разрыв в понимании между html, приходящим с сервера, и javascript, список id кнопок нужно всё же формировать с помощью php, потом на основании этого списка генерировать html для кнопок, а потом передавать этот список в javascript-словарь для организации клиентских функций.

Можно, однако, в этом случае упростить клиент-серверное взаимодействие. Для этого нужно задать простой вопрос: зачем php знать об идентификаторах визуальных кнопок? Когда мы генерируем форму отправки сообщений, php нужно знать имена элементов формы (точнее, связанные с ними некоторые свойства полей mysql) – чтобы правильно их обрабатывать перед отправкой в базу. Но зачем php знать об отдельных действиях визуального редактора? Редактор генерирует какой-то html-код (страницы), и потом на сервере php имеет дело с этим кодом как с единым целым. PHP без разницы, сколько раз пользователь нажмёт кнопку «Жирный», и что при этом будет происходить с кодом. Так неизбежно мы приходим к мысли полностью генерировать html-код кнопок визуального форматирования на стороне клиента.

PHP и CSS

Стратегия отношений php и javascript вырисовывается более-менее понятная: 1) всё управление страницей делать исключительно на javascript, если нет суровой необходимости вмешивать сервер; 2) если ну никак нельзя избежать дублирования сущностей, эти сущности обязательно нужно передавать второй стороне в явном виде (избегая неявных договорённостей). Одним из признаков воплощения этой стратегии (хорошей практики веб-программирования) является так называемый ненавязчивый javascript, при котором в html-коде отсутствуют всякие onclick и onmouseover. Ненавязчивый javascript, конечно, и внешне красив, но он является лишь побочным эффектом правильной передачи значений между сервером и клиентом.

Гораздо хуже обстоит дело с передачей сущностей от php к CSS. Для javascript можно загрузить отдельный файл словаря с сущностями в виде массива, а потом основной js-файл сайта использует этот словарь в своих функциях. Для CSS нельзя создать файл «словаря», потому что у CSS нет функций, которые могли бы его использовать. Поэтому целый ряд идентификаторов (или класснэймов) обрабатываются в CSS параллельно, то есть с использованием тех самых неявных договорённостей. С одной стороны, это принципиально правильно (php не должне знать, как будет выглядеть тот или иной элемент). С другой, приходится держать «в памяти» ряд объектов. Создать надёжную систему передачи значений между генерирующим скриптом (php или js) и CSS – вот наша задача.

D.M., admin

Комментарии