You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@cocoon.apache.org by Steve K <sh...@mm.st> on 2003/11/22 21:17:55 UTC

Keeping JavaScript out of the HTML (was Re: Refactoring woody styling)

If the goal of Woody is to include some rich client side functionality, 
I would like to throw my two cents in with a little experience I've had 
implementing something similar.  I created a form framework that 
required some support for non-standard HTML widgets, for example, a 
multi-select drop down (MSDD).  I also wanted to keep the javascript 
code as separate as possible so it could be easily maintained, and make 
it easily reusable so having many MSDDs on one page would be trivial to do.

What I wound up doing is implementing the controller code for the MSDD 
as a JavaScript class.  The class would take references to the three 
elements participating in the UI of the MSDD (a select box to handle the 
actual selection, a text input field to show the comma-separated list of 
selected items, and a button to toggle the appearance of the select 
box).  It would then dynamically attach the various event handlers to 
the UI elements to methods in the class.  This way the controller code 
remains loosely coupled with the HTML -- there is no need to attach 
onXXX attributes to the HTML elements.  This also allowed me to keep the 
class in a separate source file to be included via XSLT or a client side 
<script> tag.

In retrospect, one thing I would probably do differently is have the 
javascript dynamically create the additional UI elements needed for the 
display of the MSDD (text field, button).  This way a browser that does 
not support javascript would only see the select field and not the 
additional elements needed to produce the MSDD effect.

So the form's xslt would have to do 2 things.  First, see if there are 
any MSDDs in the form, and if so, include the MSDD class either directly 
or produce a <script> tag that includes it:

** Note that these code samples taken from the XSLT of my custom form 
stuff and are not intended to have anything to do with woody **

<xsl:if test="form:fields/form:field[@style-hint = 'multidropdown']">
     <xsl:call-template name="multidropdown_js"/>
</xsl:if>

Second, it needs to create an instance of the class for each use of the 
MSDD.  So the following code would have to be generated for each MSDD 
element in a place that would be executed on load:

<xsl:for-each select="form:fields/form:field">
     <xsl:if test="@style-hint = 'multidropdown'">

var <xsl:value-of select="@name"/>_multidropdown = new Multidropdown(
     document.getElementById("<xsl:value-of select="@name"/>_input"),
     document.getElementById("<xsl:value-of select="@name"/>_button"),
     document.getElementById("<xsl:value-of select="@name"/>")
);
     </xsl:if>
</xsl:for-each>

And finally, I'll leave you with the implementation of the MSDD class. 
I hope that this can be of use to someone :)

<xsl:template name="multidropdown_js">
<![CDATA[
function Multidropdown(p_input, p_button, p_select) {

     var input  = p_input;
     var button = p_button;
     var select = p_select;

     this.visible = false;
     this.that    = this;

     var input_position = getAbsolutePosition(input);

//    select.style.display = "none";
     select.style.top   = input_position[1] + input.offsetHeight + 
input.style.borderWidth;
     select.style.left  = input_position[0];
     select.style.width = input.offsetWidth;

     this.hide = function() {
         var that = this.that;
         select.style.display = "none";
         that.visible = false;
     }

     this.show = function() {
         var that = this.that;
         select.style.display = "block";
         select.focus();
         that.visible = true;
     }

     this.toggle = function() {
         var that = this.that;
         if(!that.visible)
             that.show();
         else
             that.hide();
     }

     this.update = function() {
         var a = [];
         var o = select.options;
         for(var i = 0; i < o.length; i++)
             if(o[i].selected)
                 a.push(o[i].text);
         input.value = a.join(", ");
     }

     this.selectOnBlur = function(e) {
         var that = this.that;
         that.hide();
         that.update();
     }

     this.buttonOnClick = function(e) {
         var that = this.that;
         that.toggle();
     }

     this.selectOnChange = function() {
         var that = this.that;
         that.update();
     }

     button.that = this;
     button.onclick = this.buttonOnClick;

     select.that = this;
     select.onblur = this.selectOnBlur;

//    select.onchange = this.selectOnChange;

     this.update();
}
]]>
</xsl:template>