user1643156
user1643156

Reputation: 4537

css3pie messes up DOM, results in jQuery selector errors

In order to get CSS3 effects (border-radius, box-shadow...) on IE 6/7/8, I'm using css3pie.

However, css3pie generates some css3-container (v1) / css3pie (v2) tags in DOM, which disorders the expected architecture. Here is an example:

CSS

pre {
    border: 1px solid #aaa;
    border-radius: 5px;
    behavior: url(pie.htc);
}

HTML

<div class="foo">bar</div>
<p class="getme">paragraph</p>
<pre>preformatted</pre>

jQuery

// undefined        expected: getme
alert($("pre").prev().attr("class"));

// css3-container   expected: p
alert($("pre").prev()[0].tagName);

// getme            expected: foo
alert($("pre").prev().prev().attr("class"));

// 4                expected: 3
alert($("body").children().size());

// will not set     expected: Impact
$("p+pre").css({fontFamily: "Impact"});

// it almost affects all such jQuery selctors

The actual generated source is like this:

<DIV class="foo">bar</DIV>
<P class="paragraph">paragraph</P>
<css3-container...>
    <border...>
        <shape...><stroke></stroke><stroke></stroke></shape>
    </border>
</css3-container>
<PRE>preformatted</PRE>

Has anyone encountered this kind of problems? Any workaround? Is there an alternative to css3pie to get CSS3 working on IE 6/7/8?

Upvotes: 6

Views: 800

Answers (5)

Giona
Giona

Reputation: 21114

I tried using CSS3PIE too, and faced similar problems (mostly with jquery and mediaqueries). I found no easy/practical solution for all the problems it causes, indeed.

My advice would be to use Modernizr's load to progressively enhance older IE's user experience. It requires an harder/longer process, as you've to setup a single polyfill for each and every CSS3 feature. As mario.tco already told you, there's a list of cross-browser polyfills on Modernizr's repo. And here's a list of feature detection snippets.

Also have a look at html5please and caniuse.

In regard to IE6 and 7, unless your site statistics indicate something different, usage rates are below 1% on average (with some exceptions, check ie6countdown), so you can almost ignore them. However, with conditional comments you can target each IE<10 version with specific fallbacks.

Keep in mind that you don't really need to have box-shadows and other visual decorations (unless they are needed for usability) on IE<9. Indeed, any fallback will probably cause a huge performance problem (think about what hardware an IE7 user could have). Websites don't need to look exactly the same in any browser.

Upvotes: 3

Jeffery To
Jeffery To

Reputation: 11936

This is probably not the answer you're looking for, but instead of trying to get jQuery to ignore PIE's injected elements, I recommend (re)writing your jQuery to use classes / IDs more and be less dependent on page structure. This has the benefit of making your code more resilient against other page structure changes, as well as making your code a bit more portable and reusable.

When you must traverse the DOM, most (if not all) of jQuery's traversal methods include a filter selector argument that you can use to exclude PIE's elements, for example:

$("pre").prevUntil('*', 'not(css3-container)')

$("body").children('not(css3-container)')

Upvotes: 1

Geek Num 88
Geek Num 88

Reputation: 5312

Instead of just using the raw prev() add a CSS selector to it to narrow down the search

$("pre").prevUntil('p').attr("class");
// OR
$("pre").prevUntil('.paragraph').attr("class");

If you are going to use a CSS3 "hack" to make IE 6/7/8 behave correctly don't try and rely on expected DOM structure when walking the DOM try to be more specific.

EDIT

changed the prev() function call to prevUntil()

Upvotes: 0

codefactor
codefactor

Reputation: 1656

CSS3PIE is a very useful and powerful way to simulate CSS3 rounded corners - and in my company it is the one that we chose, but there are many other ways to do it.

The way CSS3PIE creates the rounded corners it will create the <css3-container> tag as the previous sibling to the element that has the behavior attribute, so it will change DOM structure and break your prev() calls. The css-container is important because it is a VML drawing of the rounded corner background behind your <pre> tag.

One way you could do this would be to wrap your <pre> tag in something else like a <div> and then use that <div> to navigate the DOM using the prev() function.

Another way you could do this would be to create a jQuery plugin like this

/* This adds a plugin prevPie and nextPie - it is the same as the
   existing prev and next, but it will ignore css3-containers. */
(function($){
    function addPlugin(name) {
        $.fn[name + 'Pie'] = function() {
            var result = [];
            this[name]().each(function(i,el){
                if (el.tagName == 'css3-container') {
                    var val = $(el)[name]()[0];
                    val && result.push(val);
                } else {
                    result.push(el);
                }
            });
            return $(result);
        }
    }
    addPlugin('prev');
    addPlugin('next');
})(jQuery);

Now the following should work like you wanted it to in all browsers.

// undefined        expected: getme
alert($("pre").prevPie().attr("class"));
// css3-container   expected: p
alert($("pre").prevPie()[0].tagName);

// getme            expected: foo
alert($("pre").prevPie().prevPie().attr("class"));
// P                expected: div
alert($("pre").prevPie().prevPie()[0].tagName));

Upvotes: 2

Related Questions