OpenClinica User Manual/StylingWithJQuery

Sometimes it happens that a page generated by OpenClinica does not look like you want it to. We can style headers and descriptive text around input elements using the recipes in StylingACRF. If we want to style the input elements themselves we have to do a little more work. By adding class names to the items we define in Excel and mixing in a little javascript from the jQuery library, we can style input elements like text boxes, text areas and select fields. OpenClinica 3.1 loads the jQuery library with every CRF page. In earlier versions of OpenClinica jQuery is not loaded, so we have to do that "manually". If you want to read more about jQuery, follow this link http://docs.jquery.com/Main_Page

Compatibility

edit

OpenClinica is not tested with all browser versions, styling with jQuery will not work with most untested browsers. Please consult the following table to see which browsers can be used.

Browser Compatible?
Internet Explorer 7 Yes
Internet Explorer 8 Yes
Internet Explorer 9 Yes
Firefox >= v3.0 Yes
Google Chrome No
Opera No
Safari No

Items in non-repeating groups

edit

We'll start with a script for "normal" items, i.e. items in non-repeating groups:

<script>
/* 
    Purpose: to style input items on the entry screen with CSS 
    Instructions:
    Add this script  to your CRF Design Excel sheet once.
    A good place would be the left item text of the first item 
    For every item that you want to style put a span or div element around the left or right item text, 
    add a class name to this element. 
    Example: <span class="MyClassForTextInput">Description of the field</span> (Again: this is added to left_item_text 
    or right_item_text in the Excel sheet)
    For every class you have defined add a function call to the styleItem function in this script, below the function definition.
    Example: styleItem('.MyClassForTextInput','input',{'width':'100px','padding-left':'15px'});
    Add the CSS styling you need through the CSS map parameter of the function. See below for the parameter descriptions.
	
    Remark: since jQuery is not loaded during CRF preview, you can only see the results in the saved CRF version during data entry/review.
*/
    jQuery(document).ready(function($) { 
        function styleItem(pClassSelector, pElementType, pCSSMap) {
	/*
	    This function adds styling to an input item.
	    Parameters:
	        pClassSelector: String. The name of the class of the item defined in the left or right item text. 
	   			    Include the dot (.) in the class name. 
                                    Example: '.MyClassForTextInput'
	        pElementType:   String. The html type of the response-type item that need styling, 
	   	                    mostly this is 'input', for 'single-select' and 'multi-select' the type is 'select', for textarea it is 'textarea'
	    			    Example: 'select'
                pCSSMap:        Dictionary. CSS key/value pairs to style the element. Written as: {'key':'value','key2':'value2',...}
	                            Example: {'width':'50px','background-color':'red'}
        */
	    // find and apply styling to the input element
	    $(pClassSelector).parent().parent().find(pElementType).css(pCSSMap);
	}
		
	/* example calls to styleItem */ 
	//styleItem('.MyClassForTextInput','input',{'width':'100px','padding-left':'15px'});
	//styleItem('.MyClassForSelectInput','select',{'width':'80px','font-size':'120%','color':'orange'});
	//add your functions calls on the next line

    });
</script>

In practice

edit

In practice you don't need all the included comments, just the lines:

<script src="includes/jmesa/jquery.min.js">// for OC versions before 3.1.4, use jquery-1.3.2.min.jsĀ !</script>
<script type="text/javascript">
    jQuery(document).ready(function($) { 
        function styleItem(pClassSelector, pElementType, pCSSMap) {
            $(pClassSelector).parent().parent().find(pElementType).css(pCSSMap);
        }
        styleItem('.class1','input',{'width':'80px','background-color':'red'});
        styleItem('.class2','input',{'width':'40px','background-color':'green'});
        styleItem('.class3','textarea',{'width':'40px','height':'40px','background-color':'#F3F2E8'});
        styleItem('.class4','select',{'background-color':'#A14141'});
   });
</script>

will do. The lines starting with styleItem are the lines that we add ourselves to define the styling we want for an element. The rest of the script must not be modified.


We will put this script in the LEFT_ITEM_TEXT of the first item. And after that you can still put all the text you would like to have there. Have a look at the four lines that start with styleItem. Here is where we define the four classes we want to use in our form. class1 and class2 are just names, but input is the type of element that we can apply these styles to. Then comes a list of things of which the style is made and you can add endlessly to this. In the example just width and background-color are used. Then class3 is one to be used for a text-area. Finally class4 can be used with a select, single or multi.

Applying the style

edit

The function is in place, so we want to apply it to an input and we do this by putting in the LEFT_ITEM_TEXT or the RIGHT_ITEM_TEXT a span with class class1

 
the CRF

And this is the result:

 
the result

As you can see the first class is used on again on row 7 and this is possible, because it is also an input. And the other way round: you can not use the same style for both an input and a text-area, if you want them both to have a width of 40px, you must define two classes.

How does it work?

edit

The way the script works is as follows. The span in our LEFT_ITEM or RIGHT_ITEM_TEXT is part of a table. And in this table is also the input we would like to apply a style to. First we refer to the parent of the span, which is the [td]. The parent of this element is the [tr]. Now we can go through all the elements of this [tr] by type, with the find(pElementType) and of course we're looking for the inputs and that's only one.

Items in a repeating group

edit

In the case of items in a repeating group the situation is a bit different. Have a look at the documented function.

<script>
/* 
    Purpose: to style input items in grouped items on the entry screen with CSS 
    Instructions: 
    Add this script  to your CRF Design Excel sheet once.
    A good place would be the left item text of the first item of the group. This becomes the column header in the screen.
    Warning: Don't put this script in right_item_text of a grouped item because that will not be rendered to the screen.
    For every column that you want to style put a span or div element around the left_item_text. Again: right_item_text is useless here.  
    Add a class name to this element. 
    Example: <span class="MyClassForFirstColumn">Description of the first column</span> (Again: this is added to left_item_text)
    For every class you have defined add a function call to the styleGroupColumnN function in this script, below the function definition.
    Example:styleGroupColumnN('.MyClassForFirstColumn','1','input',{'width':'20px','color':'white','font-weight':'120%','background-color':'blue'});
    Add the CSS styling you need through the CSS map parameter of the function. See below for the parameter descriptions
    
    Remark: since jQuery is not loaded during CRF preview, you can only see the results in the saved CRF version during data entry/review.
*/
    jQuery(document).ready(function($) { 
	function styleGroupColumnN(pClassSelector, pN, pElementType, pCSSMap) {
	    /*
                This function adds styling to an input items in a group.
	        Parameters:
	        pClassSelector: String. The name of the class of the item defined in the left item text. 
	    	    			Include the dot (.) in the class name. Example: '.MyClassForFirstColumn'
	        pN:		The ordinal number of the column to be styled. The first column is '1', the second '2' and so on.
	    				Example: '1'
	        pElementType: 	String. The html type of the response-type item that need styling, 
	    				mostly this is 'input', for 'single-select' and 'multi-select' the type is 'select', for textarea it is 'textarea'
	    				Example: 'select'
                pCSSMap:	Dictionary. CSS key/value pairs to style the element. Written as: {'key':'value','key2':'value2',...}
		 			Example: {'width':'50px','background-color':'red'}
            */
	    
            // apply styling to the column header (the td element that holds the span or div with our class name)
	    $(pClassSelector).parent().css(pCSSMap);
	    // find and apply styling to all the input items in the column that we want to style
	    $(pClassSelector).parent().parent().parent().parent().children('tbody').children('tr').children('td:nth-child(' + pN + ')').children(pElementType).css(pCSSMap);
	}

	/* example calls to styleGroupColumnN */ 
	//styleGroupColumnN('.col2','2','input',{'width':'20px','color':'white','font-size':'120%','background-color':'blue'});
	//styleGroupColumnN('.col3','3','input',{'width':'60px'});
	//styleGroupColumnN('.col4','4','select',{'width':'120px','font-size':'120%','color':'green','background-color':'black'});
        //add your functions calls on the next line
    });
</script>

In practice

edit

We strip the (useful) comments and put the rest of the script in the first LEFT_ITEM_TEXT, because the contents of RIGHT_ITEM_TEXT are not used in repeating groups.

<script>
    jQuery(document).ready(function($) { 
        function styleGroupColumnN(pClassSelector, pN, pElementType, pCSSMap) {
            $(pClassSelector).parent().css(pCSSMap);
            $(pClassSelector).parent().parent().parent().parent().children('tbody').children('tr').children('td:nth-child(' + pN + ')').children(pElementType).css(pCSSMap);
        }
        styleGroupColumnN('.colclass1','2','input',{'width':'20px','color':'white','font-size':'120%','background-color':'blue'});
        styleGroupColumnN('.colclass2','3','input',{'width':'60px'});
        styleGroupColumnN('.colclass3','5','select',{'width':'120px','font-size':'120%','color':'green','background-color':'black'});
    });
</script>

Again we define styles, colclass1 to 3 , two for inputs and one for a select. There is however an extra parameter, to indicate the column number.

Applying styles to columns

edit

Take a look at the screenshot of the XL-sheet: just as straightforward as the single-item one.

 
the result

And this is the result:

 
the result

Keep in mind that you need to style every column individually. If you want to style X columns identically, you will need X calls to the function styleGroupColumnN with the same CSS parameter. Of course you can use a variable. This improves the maintainability of your code: if you have to make a change to the styling you only have to do it once. Here is an example that uses a variable for the styling parameter:

<script>
    ...  
    ...
    var CSSForMultipleColumns = {'width':'60px', 'margin-left':'20px'};
    styleGroupColumnN('.col3','3','input',CSSForMultipleColumns);
    styleGroupColumnN('.col4','4','input',CSSForMultipleColumns);
</script>

One last thing: do not forget to change the column numbers in the calls to styleGroupColumnN after you have inserted a new column.

How it works

edit

This is a bit harder to explain, but it's something like: go four levels up (parent x 4) and then you're at the level of the table of your repeating group. Of this object, take the tbody. Of this, take all tr's. Of these, take the all n-th td's. Of these, take all the elements of type input or select and apply the style. Are you still there? Whatever the workings: it works!

Items organized horizontally

edit

In order to style items that use COLUMN_NUMBER to place them horizontally on the page, use the code below in the same way as before in your LEFT_ITEM_TEXT. The only difference is that we can now style both the label as well as the input itself with the parameters: pCSSMapLabel and pCSSMapInput respectively. This allows us, for example, to set a different width for the label and the item it belongs to.

<script>
    jQuery(document).ready(function($) { 
        function styleItemHorizontally(pClassSelector, pElementType, pCSSMapLabel,pCSSMapInput) {
            $(pClassSelector).parent().css(pCSSMapLabel);
	    $(pClassSelector).parent().parent().find(pElementType).css(pCSSMapInput);
	}
        // examples:
	styleItemHorizontally('.hori1','input',{'width':'40px'},{'width':'40px', 'background-color':'#F0F0F0'});
        styleItemHorizontally('.hori2','input',{'width':'30px'},{'width':'60px', 'background-color':'#F0F0F0'});
        styleItemHorizontally('.hori3','input',{'width':'80px'},{'width':'60px', 'background-color':'#F0F0F0'});
    });
</script>
<span class="hori1">item 1</span>