Element.Methods.visible may not return what you would expect.

I was knee deep in an old php form this week, trying to clean it up a bit, hiding and showing certain questions based on the responses of earlier questions. In addition this form had over 400 lines of JS validation that I had to turn on and off based on a questions visibility. Did I mention most of the fields and labels were stuck in a table?

(Ok…I can’t complain too much, it was marked up very nice and clean.)

So I’m hacking away at this validation and I need to only validate visible fields. So naturally I try something like this:

if($('fieldId').visible() && oldConditions) {
  throw an error;
}

Seemed pretty simple to me…until it wasn’t working. Fields that I could not see in my browser were still being validated. If I can’t see it, doesn’t that mean it is NOT visible.

According to prototype, I’m way off. The ‘visible’ method only tests THE SINGLE ELEMENT it is called on for the “style.display == ‘none’” without regard to that element’s ancestors. The problem was, my fields were visible, but the cells that contained them were not. What to do?

Extend prototpye of course! After a google and “a quick read of a short article”:http://drnicwilliams.com/2006/09/09/extend-prototype-dollar-yourself/ this is what I came up with:

Element.Methods.truelyVisible = function(element) {
  var visibleBoolean = element.visible();
  element.ancestors().each(function(el){ 
    if(!el.visible()) { visibleBoolean = false; }
  });
  return visibleBoolean;
};

Element.addMethods();

Quickly, it runs through an element’s ancestor’s checking them for visibility and then returning where the element can actually be seen or not. Now you can do $('elementId').truelyVisible() and get whether or not an element can actually be seen in the browser. Hope you enjoy!


PS. Looking at it a day later, it could probably be more efficient like this:

Element.Methods.truelyVisible = function(element) {
  var visibleBoolean = element.visible();
  if(visibleBoolean) {
    element.ancestors().each(function(el){ 
      if(!el.visible()) { visibleBoolean = false; }
    });
  }
  return visibleBoolean;
};

Element.addMethods();


PPS. It gets even better…I should blog code more often:

Element.Methods.truelyVisible = function(element) {
  return (element.visible() && !element.ancestors().any(function(el){ return !el.visible(); })) 
};

Element.addMethods();
  1. markmcspadden posted this