You are viewing an old version of this page. View the current version.

Compare with Current View Page History

« Previous Version 4 Next »

Table of contents

Scenario

Sometimes, it is useful for text to be displayed when a user is in a form element, and hidden when they leave the field. An example would be a prompt which offers help about the expected field contents, but which generally doesn't need to be displayed, since displaying them all at once would clutter up the interface.

This article will cover how to achieve this in your wicket page.

Overview of Options

Three basic ways to achieve this functionality are:

1. Create ajax behavior which gets fired when field is entered/left.

  • Pro: Most dynamic solution.
  • Con: Requires multiple round trips to server per user.

2. Tag each field with an hint attribute, and use javascript tools to display the text as needed.

  • Pro: Works as needed.
  • Con: Performed at the page level, needs components to be tweaked to match.

3. Wrap #2 inside of appropriate wicket components/behaviors.

  • Pro: Achieves goal, and works within components.
  • Con: Additional work needed over #2 for initial implementation.

Implementations

1. Create ajax behavior which gets fired when field is entered/left.

I believe this can be achieved using AjaxFormComponentUpdatingBehavior. However, after reviewing the cons, I chose not to implement it in this manner. Someone else can certainly update this if they have had success using this method.

2. Tag each field with an hint attribute, and use javascript tools to display the text as needed.

You could use any javascript library here. I am going to demonstrate using the Mootools library because it is small, modular, and does what is needed. Feel free to expand this with other options if you are familiar with them.

Here is a brief overview of the steps we will need to complete to implement this functionality:

  1. Obtain Mootools library.
  2. Customize Mootools library with a custom class.
  3. Modify our page markup.
  4. Execute the javascript function when our page loads.

And in detail:

  1. Obtain Mootools library.
    • Download the mootools library from Mootools.net.
      • The following packages must be selected for download, at a minimum (as of Mootools.v1.00).
        • Tips (and all prerequisites)
        • Window.Base
        • Dom
      • You can choose compressed or not. Typically, you'll want uncompressed until we complete our next step, and compressed when in production.
  2. Customize Mootools library with a custom class.
    • The Tips class displays a tooltip when the mouse goes over an html element of a specified class. This is similar to what we want to do, but not quite, so we will create a new class which does what we want.
    • Create a new .js file. We'll use Hints.js here.
    • If you downloaded the uncompressed version of mootools above, you can copy and paste the tips class into your own file, and make some modifications to it to achieve our desired behavior.
      1. Class name needs to be changed. In our case, we will use Hints as the class name.
        var Hints = new Class({
      2. Update the getOptions block. We want to add a new option, sizeOffset.
        getOptions: function(){
            return {
                onShow: function(tip){
        	    tip.setStyle('visibility', 'visible');
        	},
        	onHide: function(tip){
        	    tip.setStyle('visibility', 'hidden');
        	},
        	maxTitleChars: 50,
        	showDelay: 100,
        	hideDelay: 100,
        	className: 'tool',
        	offsets: {'x': 0, 'y': 3},
                sizeOffset: { 'x': false, 'y': true },
                fixed: true
            };
        },
      3. Next, we want to update the build funtion. We make several changes here. First, we display only the text, no title. Then we define the property we want this class to use as its text. Last, we change the events to focus and blur, so that our text is show when our input fields are entered and left, rather than on mouseover. Note that event of locate and start in the focus event differs from in the Tips declaration. This is due to the positioning code required.
        build: function(el){
            el.myTitle=false;
            el.myText=el.getAttribute("tooltip");
        
            el.addEvent('focus', function(event) { this.locate(el); this.start(el); }.bindWithEvent(this));
            el.addEvent('blur', this.end.bindWithEvent(this));
        },
        
      4. The next part of the Hint code that is different from the Tips code is the locate section. This section determines where the text will be displayed, and obviously this is quite different since we are not rendering at the mouse location. We use the offset and sizeOffset options to help us position the code around the control. Use the comments for guidance here.
        locate: function(el){
        
            // Locate where the event came from.  In our case:
            //  position is the upper left corner of the input that gained focus
            //  size is the size of the input that gained focus, in px
            var pos=el.getPosition();
            var size=el.getSize();
        
            // By default, position hint at top left corner of input,
            // and offset by our offset amounts.
            var left = pos['x'] + this.options.offsets['x'];
            var top = pos['y'] + this.options.offsets['y'];
        
            // If user specified that a sizeOffset should be used, additionally
            // offset the hint by the size of the control that gained focus
            if (this.options.sizeOffset['x']){ left = left + size['size']['x']; }
            if (this.options.sizeOffset['y']){ top = top + size['size']['y']; }
        
            // Assign the hint location
            this.toolTip.setStyle('left',  left + 'px');
            this.toolTip.setStyle('top', top + 'px');
        },
        
      5. Finally, don't forget to update the initialization code
        Hints.implement(new Events);
        Hints.implement(new Options);
        
    • In the end, that leaves us with the following Hints.v0.5.js file:
      var Hints = new Class({
      
          getOptions: function(){
              return {
      	    onShow: function(tip){
      	        tip.setStyle('visibility', 'visible');
      	    },
      	    onHide: function(tip){
      	        tip.setStyle('visibility', 'hidden');
      	    },
      	    maxTitleChars: 50,
      	    showDelay: 100,
      	    hideDelay: 100,
      	    className: 'tool',
      	    offsets: {'x': 0, 'y': 3},
                  sizeOffset: { 'x': false, 'y': true },
                  fixed: true
      	};
          },
      
          initialize: function(elements, options){
              this.setOptions(this.getOptions(), options);
      	this.toolTip = new Element('div').addClass(this.options.className+'-tip').setStyles({
      	    'position': 'absolute',
      	    'top': '0',
      	    'left': '0',
      	    'visibility': 'hidden'
      	}).injectInside(document.body);
      	this.wrapper = new Element('div').injectInside(this.toolTip);
      	$each(elements, function(el){
      	    this.build($(el));
      	}, this);
      	if (this.options.initialize) this.options.initialize.call(this);
          },
      
          build: function(el){
              el.myTitle=false;
      	el.myText=el.getAttribute("tooltip");
      
      	el.addEvent('focus', function(event) { this.locate(el); this.start(el); }.bindWithEvent(this));
      	el.addEvent('blur', this.end.bindWithEvent(this));
          },
      
          start: function(el){
      	this.wrapper.setHTML('');
      	if (el.myTitle){
      	    new Element('span').injectInside(
      	        new Element('div').addClass(this.options.className+'-title').injectInside(this.wrapper)
                  ).setHTML(el.myTitle);
      	}
      	if (el.myText){
      	    new Element('span').injectInside(
      	        new Element('div').addClass(this.options.className+'-text').injectInside(this.wrapper)
      	    ).setHTML(el.myText);
      	}
      	$clear(this.timer);
      	this.timer = this.show.delay(this.options.showDelay, this);
          },
      
          end: function(event){
      	$clear(this.timer);
      	this.timer = this.hide.delay(this.options.hideDelay, this);
      	event.stop();
          },
      
          locate: function(el){
      
              // Locate where the event came from.  In our case:
              //  position is the upper left corner of the input that gained focus
              //  size is the size of the input that gained focus, in px
              var pos=el.getPosition();
      		var size=el.getSize();
      
              // By default, position hint at top left corner of input,
              // and offset by our offset amounts.
              var left = pos['x'] + this.options.offsets['x'];
              var top = pos['y'] + this.options.offsets['y'];
      
              // If user specified that a sizeOffset should be used, additionally
              // offset the hint by the size of the control that gained focus
              if (this.options.sizeOffset['x']){ left = left + size['size']['x']; }
              if (this.options.sizeOffset['y']){ top = top + size['size']['y']; }
      
              // Assign the hint location
              this.toolTip.setStyle('left',  left + 'px');
              this.toolTip.setStyle('top', top + 'px');
          },
      
          show: function(){
              this.fireEvent('onShow', [this.toolTip]);
          },
      
          hide: function(){
              this.fireEvent('onHide', [this.toolTip]);
          }
      
      });
      
      Hints.implement(new Events);
      Hints.implement(new Options);
      
  3. Modify our page markup.
    • Next, we need to ensure that our javascript knows what to mark up, and what to mark it up with.
      1. Add a style class to the inputs you want to have a hint. In our case, we will use "hinted". For example:
        <input class="hinted" wicket:id="usernameField" id="username" type="text"/>
        
      2. Add an attribute that has the text you want displayed for each input element. This has to match the attribute specified in the Hints build function. In our example, we use "tooltip".
        <input class="hinted" wicket:id="usernameField" id="username" type="text" tooltip="Remember, usernames are case sensitive."/> 
        
  4. Execute the javascript function when our page loads.
    • Now that we have the java script and markup in place, we just need to use them on page load.
    • To achieve this, the script file needs to be added to the page header, and then the function needs called on page load.
      1. Add the script to the page header.
        <head>
        ...
        <script src="Hints.v0.5.js"></script>
        </head>
        
      2. Call the function on page load. In this example, any input element with the class "hinted" will display a Hint offset 10px off its right side, and 4 pixels from its top, whose text will be the input's "tooltip" attribute value.
        <head>
        ...
        <script src="Hints.v0.5.js"></script>
        <script type="text/javascript">
            window.addEvent('domready', function(){
                var myHints = new Hints($$('.hinted'), {
                offsets: {'x': 10, 'y': 4},
                sizeOffset: {'x':true, 'y':false }
              });
            });
        </script>
        </head>
        

3. Wrap Implementation #2 inside of appropriate wicket components/behaviors.

Help needed to flesh this out correctly.

  • No labels