Pastie now auto-senses if line-wrap is a bad or good idea. Feedback?
## mark a section (Learn more)
/* 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' } %> <p> 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') %> </p> <p> 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') %> </p> <p class="save"><%= link_to_remote image_tag('cancel'), :method => 'get', :url => { :action => 'cancel' } %> <%= image_submit_tag 'save' %></p> <%= 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; } }
This paste will be private.
From the Design Piracy series on my blog: