Report abuse

/*
  Validation tooltips

  Tim Lucas
  http://toolmantim.com

  See the effect at:
  http://www.viddler.com/explore/toolmantim/videos/14

  Written against the prototype & scriptaculous included in Rails 0.14.3

  Originally used with this RJS helper:

    module ValidationTooltipsHelper
      def add_validation_tooltip(id, message)
        "ValidationTooltip.add('#{id}','#{escape_javascript(message.to_a.join(', '))}');"
      end

      def remove_validation_tooltip(id)
        "ValidationTooltip.remove('#{id}');"
      end

      def remove_all_validation_tooltips
        "ValidationTooltip.removeAll();"
      end
    end

  and then used from RJS like so:

    unless @cancelled
      [:name, :email].each do |field|
        if @person.errors[field]
          page.hide "#{field}-valid"
          page << add_validation_tooltip("person_#{field}", @person.errors[field])
        else
          page.visual_effect :appear, "#{field}-valid", :duration => 0.25
          page << remove_validation_tooltip("person_#{field}")
        end
      end
    end

    if @person.valid?
      page << remove_all_validation_tooltips
      page.delay(@cancelled ? 0.5 : 1) do
        page.replace_html 'profile-view', :partial => 'view'
      end
    end

  with an obtrusive looking view like so:

    <%= form_remote_tag :url => { :action => 'save' } %>
      

Name: <%= text_field :person, :name, :length => 8 %> <%= image_tag('icon-valid', :id => 'name-valid', :style => 'display: none') %> <%= image_tag('icon-exclamation', :id => 'name-error', :style => 'display: none') %>

Email: <%= text_field :person, :email, :length => 8 %> <%= image_tag('icon-valid', :id => 'email-valid', :style => 'display: none') %> <%= image_tag('icon-exclamation', :id => 'email-error', :style => 'display: none') %>

<%= link_to_remote image_tag('cancel'), :method => 'get', :url => { :action => 'cancel' } %> <%= image_submit_tag 'save' %>

<%= end_form_tag %> */ ValidationTooltip = Class.create(); ValidationTooltip.Register = { instances: $H(), all: function() { return this.instances.values(); }, register: function(element, tooltip) { this.instances[element] = tooltip; }, get: function(element) { return this.instances[element]; }, each: function(x) { return this.instances.values().each(x); }, remove: function(element) { delete this.instances[element]; } }; ValidationTooltip.add = function(element, message, options) { if (ValidationTooltip.Register.get(element)) { ValidationTooltip.remove(element); } tooltip = new ValidationTooltip(element,message,options); ValidationTooltip.Register.register(element, tooltip); tooltip.show(); } ValidationTooltip.remove = function(element) { if (tooltip = ValidationTooltip.Register.get(element)) { ValidationTooltip.Register.remove(element); tooltip.hide(); } } ValidationTooltip.removeAll = function() { ValidationTooltip.Register.each(function(tooltip) { tooltip.hide(); }); }; ValidationTooltip.prototype = { initialize: function(element, message, options) { this.element = $(element); this.message = message; this.timer = null; this.tooltip = null; this.options = Object.extend({ idPrefix: 'validation_tooltip_', className: 'validation-tooltip', appearDuration: 0.25, fade: true, fadeDuration: 0.25, onShow: null, onHide: null }, options || {}); this.options.onShow = this.options.onShow || function(tooltip) { if (tooltip.options.fade) { new Effect.Appear(tooltip.tooltip, {duration: tooltip.options.appearDuration}); } else { Element.show(tooltip.tooltip); } }; this.options.onHide = this.options.onHide || function(tooltip) { if (tooltip.options.fade) { new Effect.Fade(tooltip.tooltip, {duration: tooltip.options.fadeDuration}); } else { Element.hide(tooltip.tooltip); } }; this.createTooltip(); this.show(); }, createTooltip: function() { this.tooltip = document.createElement("span"); this.tooltip.appendChild(document.createTextNode(this.message)); this.tooltip.id = this.id(); Element.addClassName(this.tooltip, this.options.className); this.tooltip.style.display = 'none'; this.tooltip.style.width = this.message.length / 1.5 + 'em'; this.tooltip.style.position = 'absolute'; Position.clone(this.element, this.tooltip, {setWidth: false, setHeight: false, offsetTop: -this.element.offsetHeight, offsetLeft: this.element.offsetWidth}); this.element.parentNode.parentNode.appendChild(this.tooltip); }, show: function() { this.options.onShow(this); }, hide: function() { this.options.onHide(this); }, id: function() { return this.options.idPrefix + this.element.id; } }