Safari 4 won't apply the styles of a dynamically added class

Doing some form validation within a lightbox. If a field fails the validation I use javascript (prototype) to add a class to the field’s containing div in order to apply some styles. This works just fine in every browser (including IE6) but Safari is choking on it.

The Class is being applied. Likewise, the styles ARE being applied to the elements when I look in the Web Inspector, but the styles don’t actually change unless I check/uncheck a style within the inspector.

Has anyone come across this before? Is there some way I can trigger a refresh of the rendering?

No, sorry, I really can’t share source code, but it’s all fairly standard anyhow.

Well unless we see the code we won’t know :). This could possibly be a JS issue though most likely CSS.

It’s nearly impossible to tell without any source code or a link :).

You are asking about Reflow / Repaint if you want to look it up.

Changing a class is such a common task there must be a simple error in the HTML / CSS, try validation.

I understand Ryan, but posting what amounts to hundreds of K worth of JS/CSS/HTML on the off chance that you’ll find the offending line(s) before I do really isn’t going to help much. Also might make the people I work for more than a little miffed. :slight_smile:

Thanks Mark, that’s what I expected but hadn’t been able to track anything down yet. I’ll just keep digging.

Wel even posting a link to a test page would help :). We can’t debug otherwise ;).

Can’t right now, it’s still under lock and key in our development environment. If the bug persists once it’s pushed out to production I’ll link it up.

jQuery val works just fine in Safari 4

Thanks Eric, but the site was built with Prototype, and the validation I’m using is custom. It does work in Safari, except when the form is within a lightbox. That’s obviously the variable causing the problems, but wrangling position: and display: values does nothing - the usual fixes are ineffective at triggering a repaint.

Your welcome. How is your lightbox form being called? Is the form in an outside page or hidden within the same page? I just did this, I went with the form in an outside page and poped up in the lightbox (mainly because I couldn’t get it to work inside the page). It also worked in safari with jquery. Have you tested this live? Because when using js my safari only works sometimes live.

The lightbox is inserted into the DOM via JS once the page has finished loading. Since the lightbox is a fundamental tool on the app and there can be more than one link on a page that triggers the lightbox, it’s content is loaded via AJAX after it’s called. In this instance, everything loads and is inserted properly into the DOM.

Once I start filling out the form the validation performs as expected and the JS applies the appropriate classes to the broken form fields. If I check the Web Inspector the styles are being applied, but Safari isn’t actually redrawing/repainting the elements themselves, so the background isn’t changing color, text color isn’t being applied, and some margins are being ignored. It seems to have trouble with styles that are being overridden by the new class (and before you asked, yes I’ve checked the cascade — the styles I want have the right specificity and according to the Web Inspector are the ones being show, but they actually aren’t).

I can trigger a redraw/repaint by un/checking styles (related or not/on the offending DOM elements or not) within the inspector. But that’s not good enough.

Short of throwing in some extra JS (like a display:none back to display:block flip) to trigger the repaint, do you have any suggestions?

I don’t sorry.

Disappointingly I learned that hiding/showing an element wouldn’t work because of the same basic issue — styles, whether inline or in a stylesheet, wouldn’t cause Safari to repaint.

But with big thanks to the hit new game show Will it reflow? I found a solution. I hope it helps someone else.


function bruteForceRepaint()
{
  var ss = document.styleSheets[0];
  try
  {
    ss.addRule('.xxxxxx', 'position: relative');
    ss.removeRule(ss.rules.length - 1);
  }
  catch(e){}
}

I haven’t fully tested this cross browser yet, but here’s the jist of it: I’m collecting an already existing stylesheet and adding a new rule to it that doesn’t actually apply to any elements, but the addition of this rule will cause browsers to redraw/repaint the entire page (twice — once when you add the rule, once when you take it away). This could quickly become a source of slowdown though, so I’m leaving it up to you to optimize the code for what you need. However, it works.

Edit: A couple other points — Chrome was affected as well, and addRule doesn’t work in Firefox so I wrapped it in a try/catch block. Otherwise the code is safe for IE6 as well (bless his soul).