При разработке веб-приложения иногда приходилось передавать информацию с сервера компоненту JavaScript в момент создания страницы. Я считаю, что общим решением является использование элемента <script> на странице HTML, который инициализирует компонент данными и генерируется серверным скриптом (PHP, Groovy, Python и т. Д.). Тем не менее, при написании повторно используемых компонентов JavaScript это не поддерживаемое решение.
Проблема заключается в том, что компонент может использоваться несколько раз (или даже вообще не использоваться), но каждый раз, когда компонент включается в страницу HTML, выполняется один и тот же сценарий инициализации. Это сработало бы, если бы каждый компонент был идентифицирован уникальным идентификатором, однако это не является хорошей практикой.
Я всегда стараюсь улучшить то, что кажется громоздким, поэтому в своих проектах я использую декларативный подход в теле HTML. Этот подход отменяет ответственность за инициализацию компонентов JavaScript с сгенерированной страницы во внешний файл js. Поэтому единственный элемент <script>, размещенный на странице HTML, — это ссылка на внешний файл js, который вызывает функцию инициализатора после загрузки документа.
Окончательное решение может выглядеть так:
<div class="my-javascript-button"> <div class="config" style="display: none;"> <div class="title">Go to www.google.com</div> <a class="url" href="http://www.google.com"/> </div> </div>
или же
<div class="my-javascript-button"> <div class="config" style="display:none;"> <!-- JSON object: --> { "title" : "Go to www.google.com", "url" : "http://www.google.com" } </div> </div>
Чтобы прочитать о лучшем способе его кодирования, перейдите непосредственно к решению «Золотое сечение».
Описание проблемы:
- Данные в скрипте сервера, который генерирует страницу динамически (PHP, Ruby, Python, …)
- HTML-страница, сгенерированная скриптом
- Javascript включен в HTML-страницу (JavaScript является статическим, а не генерируется)
Поэтому конфигурация из серверного скрипта может быть передана на HTML-страницу (отображаемую на странице), но не в статические JavaScript-скрипты.
Мы хотим создать инкапсулированные компоненты HTML + JavaScript, которые могут быть вставлены один или несколько раз в сгенерированную HTML-страницу и могут быть переданы в качестве объекта конфигурации из сценария на стороне сервера для настройки их поведения.
Простое решение с использованием JSON (все еще громоздко)
Требования:
- требует поддержки JSON и случайной генерации в скрипте сервера
- требуется фрагмент кода JavaScript на странице HTML
Оба требования делают решение немного обременительным, но понимание этого решения поможет вам понять идею.
Описание:
- Для каждого фрагмента HTML-кода (который помещает компонент в сгенерированную страницу), сгенерируйте случайный текстовый идентификатор
-
def randomId = getRandomId('my-javascript-button-')
-
- Пометьте этот фрагмент HTML с помощью randomId, либо по идентификатору атрибута, либо как класс css, либо как другой атрибут:
-
<div class="my-javascript-button ${randomId}"> ... rest of html goes here </div>
-
- Во внешнем файле JavaScript для компонента создайте функцию инициализатора для этого типа конфигурации, которая принимает два параметра: randomId для идентификации целевого элемента html и объект, содержащий конфигурацию:
-
function initializeMyJavascriptButton(id, config) { }
-
- Вызовите функцию инициализатора со страницы, передавая randomId и объект сценария, закодированный в JSON:
- Пример в Grails GSP:
initializeMyJavascriptButton("${randomId}" , ${config as JSON} );
- Пример в Grails GSP:
Полученные результаты:
- Код JavaScript, который может быть эффективно передан конфигурации
- Элемент HTML и его конфигурация визуально разделены в исходном коде HTML как блоки HTML и JavaScript — они связаны сгенерированным случайным идентификатором
Декларативное решение с использованием невидимой структуры DOM
Простое решение JSON работает, но его нелегко обслуживать и читать. Сгенерированные идентификаторы — нам это действительно нужно? Разве мы не можем просто пометить все компоненты общим классом? Где находится конфигурация компонента в другом блоке HTML? Почему бы не поместить его непосредственно в место, где компонент объявлен, как его атрибуты, или, по крайней мере, во вложенный блок? Кроме того, генерация кода JavaScript не является идеальной идеей, есть ли способ поместить весь код в отдельный файл или даже в библиотеку?
В конце концов, основной вопрос: зачем нам нужен JavaScript, чтобы просто объявить компонент вместе с его атрибутами, когда HTML достаточно богат, чтобы сделать это?
Представьте себе эту декларацию:
<div class="my-javascript-button" title="Go to www.google.com" url="http://www.google.com"> </div>
Видишь, о чем я?
Хотя приведенный выше код не является допустимым кодом XHTML, мы можем очень близко подобраться к этому, не нарушая правил XHTML. Фактически, мы можем использовать вложенный невидимый элемент, который будет доступен в дереве DOM из JavaScript, но не повлияет на отображение страницы.
Требования:
- пользовательский метод в сценарии сервера для генерации HTML-элементов для отображения данных на сервере
- пользовательский метод для чтения значений из HTML-элементов на странице
Обе эти пользовательские методики могут быть извлечены в серверную часть и JavaScript API, который можно использовать повторно.
Описание:
- Для каждого фрагмента кода HTML (который помещает компонент в сгенерированную страницу), создайте вложенный элемент div со специальным классом «config», которому присвоен стиль css «display: none;» :
- div с классом «config» будет невидимым в документе и не будет влиять на то, как генерируется страница:
<div class="my-javascript-button"> <div class="config" style="display: none;"> </div> </div>
- div с классом «config» будет невидимым в документе и не будет влиять на то, как генерируется страница:
- Внутри div с классом «config» вы можете поместить любые элементы DOM, идентифицировать их с помощью атрибута класса, а затем присвоить им значение, либо как один из допустимых атрибутов, либо как тело элемента:
<div class="my-javascript-button"> <div class="config" style="display: none;"> <div class="title">My title</div> <a class="url" href="http://my.url"/> <ul class="listOfTargets" > <li>target1</li> <li>target2</li> </ul> </div> </div>
- Если вам нужно хранить вложенные объекты, вы также можете вкладывать элементы или даже вкладывать список элементов. Фактически вы можете создать любую HTML-структуру, которая будет каким-то образом хранить нужные значения. Вы можете сделать его более читабельным, используя соответствующие элементы HTML (ul для списков, a для URL), или всегда использовать элементы div.
- Во внешнем файле JavaScript определите инициализатор, который можно выполнить для инициализации конфигурации для всех целевых элементов на html-странице. Этот инициализатор найдет все целевые элементы, и для каждого из них он найдет элемент «config» и прочитает значения из поддерева DOM этого элемента.
- Функция инициализатора будет выполнена один раз после загрузки html-страницы (например, с помощью jQuery’s $ (document) .ready ()). Лучше всего выполнить инициализатор во внешнем файле JavaScript и убедиться, что он выполняется только один раз (вы можете использовать глобальную переменную или отметить все элементы конфигурации при первом выполнении, чтобы конфигурация читалась только один раз).
Полученные результаты:
- Конфигурация всегда хранится в целевом элементе html, независимо от того, поддерживает ли браузер javascript (хотя это довольно неактуально, поскольку конфигурация без javascript бесполезна)
- конфигурация декларативна, поэтому ее можно проанализировать любым альтернативным методом или даже внешним HTML-анализатором (например, в модульных тестах)
- связь между элементом html и его конфигурацией видна с помощью инспектора DOM прямо внутри элемента html
- нет стандартного способа записи и чтения конфигурации в и из элементов HTML
- может быть медленнее, чем прямое преобразование данных сервера в JSON
Как это решение довольно приятно и читабельно. Это может быть достаточно или даже желательно в некоторых случаях. Тем не менее, он имеет некоторые недостатки по сравнению с решением Simple JSON: код легче читать, но его написание занимает больше времени, а также чтение конфигурации с помощью JavaScript может выполняться медленнее, чем анализ JSON.
Поэтому, как всегда, есть золотая середина, которая решит все. Или есть?
Решение Golden Mean — декларативный подход с JSON
Идея здесь состоит в том, чтобы стандартизировать способ хранения данных в рамках вложенного элемента конфигурации, используемого в декларативном решении. Вместо написания config с использованием элементов DOM мы бы использовали JSON, который легко читается с помощью JavaScript.
Требования:
- Как и в решении Simple JSON, это решение требует поддержки JSON в серверном скрипте, но не использует случайные числа и не требует добавления JavaScript для инициализации компонента.
- Как и в декларативном решении, требуется некоторый пользовательский код. Однако на этот раз никакой специальной методики на стороне сервера не требуется, а на стороне клиента требуется только функция для извлечения блока конфигурации в объявлении компонента (пара строк кода JavaScript для повторного использования)
Описание:
Это решение аналогично декларативному решению. На самом деле, он также декларативный, но использует один простой трюк вместе с JSON:
Вместо того, чтобы кодировать данные в структуру HTML, мы можем закодировать конфигурацию в JSON и поместить ее как тело элемента config, например так:
<div class="my-javascript-button"> <div class="config" style="display: none;"> ${it as JSON} </div> </div>
JSON должен быть совместим с XHTML и не должен нарушать его структуру. Если мы не уверены, лучше поместить код JSON в блок CDATA.