Transparently Transfer Data from Server Script to Client JavaScript
Join the DZone community and get the full member experience.
Join For FreeWhen
developing a web application, sometimes I needed to pass information
from the server to a JavaScript component at the moment when the page is
built. I believe that a general solution is to use a <script>
element in the HTML page, which initializes the component with data and
is generated by a server-side script (PHP, Groovy, Python, etc.).
However, when writing reusable JavaScript components, this is not a
maintainable solution.
The
problem is that the component may be used multiple times (or even not
at all), but every time the component is included in the HTML page, the
same initialization script is executed. It would work if every component
was identified by a unique ID, however, this does not seem to be a good
practice.
I
always try to improve what appears cumbersome, so in my projects I'm
using a declarative approach within HTML body. This approach reverses
the responsibility of initializing JavaScript components from the
generated page to an external js file. Therefore, the only
<script> element placed within the HTML page is a link to the
external js file, which calls an initializer function after the document
is loaded.
The final solution may look like this:
<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>or
<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>
To read about best way how to code it, jump directly to Golden Mean Solution.
Description of the Problem:
- Data in server script, which generates page dynamically (PHP, Ruby, Python,...)
- HTML page generated by the script
- Javascript included in the HTML page (JavaScript is static, not generated)
We want to create encapsulated HTML+ JavaScript components, which can be inserted once or multiple times into a generated HTML page and can be passed as a configuration object from the server side script to configure their behavior.
Simple solution using JSON (still cumbersome)
Requirements:
- requires JSON and random generation support in server script
- requires piece of javascript code in HTML page
Description:
- For each piece of HTML code (which places the component into generated page), generate a random textual identifier
def randomId = getRandomId('my-javascript-button-')
- Mark that piece of HTML with randomId, either by attribute id, or as css class, or other attribute:
<div class="my-javascript-button ${randomId}"> ... rest of html goes here </div>
- In
the external JavaScript file for the component, create an initializer
function for this configuration type, which takes two parameters:
randomId to identify the target html element, and an object holding the
configuration:
function initializeMyJavascriptButton(id, config) { }
- Call the initializer function from the page, passing in randomId and script object encoded to JSON:
- example in grails GSP:
initializeMyJavascriptButton("${randomId}" , ${config as JSON} );
- example in grails GSP:
Results:
- JavaScript code, which can be efficiently passed configuration
- HTML element and its configuration are visually separated in the HTML source code as HTML and JavaScript blocks - they are coupled by generated random identifier
Declarative Solution using invisible DOM structure
The simple JSON solution works, but is not easily maintainable nor readable. Generated identifiers - do we really need this? Can't we just mark all components with a common class? Where is the configuration of the component, in a different HTML block? Why not put it directly at the place where the component is declared, as its attributes, or at least in nested block? Also, generating JavaScript code is not a neat idea, is there a way to put all code into a separate file, or even a library?After all, the basic question: Why do we need JavaScript just to declare a component together with its attributes, when HTML is rich enough to do it?
Imagine this declaration:
<div class="my-javascript-button" title="Go to www.google.com" url="http://www.google.com"> </div>See what I mean?
Although the above code is not a valid XHTML code, we can get really close to something like this without breaking XHTML rules. In fact, we can use a nested invisible element, which would be accessible in the DOM tree from JavaScript, but will not affect how the page is rendered.
Requirements:
- custom technique in server script to generate HTML elements to reflect data on server
- custom technique to read values from html elements in the page
Description:
- For
each piece of HTML code (which places the component into generated
page), create a nested div element with special class "config", which is
given css style "display: none;" :
- div with class "config" will be invisible in the document and will not have any impact on how the page is generated:
<div class="my-javascript-button"> <div class="config" style="display: none;"> </div> </div>
- div with class "config" will be invisible in the document and will not have any impact on how the page is generated:
- Inside div
with class "config", you may put any DOM elements, identify them using
class attribute, and then assign a value to them, either as one of valid
attributes, or as a body of the element:
<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>
- If you need to store nested objects you may also nest elements or even nest a list of elements. You may in fact create any HTML structure, which will somehow store desired values. You may make it more readable using appropriate HTML elements (ul for lists, a for urls), or always use div elements.
- In an external JavaScript file, define an initializer, which can be executed to initialize configuration for all target elements in the html page. This initializer will find all the target elements, and for each of them it will find teh "config" element and read values from the DOM subtree of this element.
- The initializer function will be executed once after html page is loaded, (e.g. using jQuery's $(document).ready() ). It is best to execute the initializer within the external JavaScript file and ensure that it is executed only once (you may use a global variable, or mark all config elements on first execution, so that config is read only once).
Results:
- configuration is always stored within the target html element, regardless if the browser supports javascript (although this is rather irrelevant, as the configuration has no use without javascript)
- configuration is declarative, therefore it can be parsed with any alternative technique or even with external HTML analyzer (e.g. in unit tests)
- coupling between the html element and its configuration is visible using the DOM inspector right within the html element
- no standard way of writing and reading configuration to and from html elements
- may be slower than direct conversion of server data to JSON
Golden Mean Solution - declarative approach with JSON
Requirements:
- As in the Simple JSON solution, this solution requires JSON support in server script, but does not rely on random numbers and does not require putting any JavaScript to initialize a component
- As in the Declarative solution, requires some custom code. However, this time, no special technique is necessary on server side, and on client side, only a function to retrieve configuration block within a component declaration is necessary (couple of lines of reusable JavaScript code)
Description:
<div class="my-javascript-button"> <div class="config" style="display: none;"> ${it as JSON} </div> </div>
Published at DZone with permission of Ondro Mihalyi, DZone MVB. See the original article here.
Opinions expressed by DZone contributors are their own.
Comments