Properly degrading JavaScript Effects with Script.aculo.us

I recently decided to use Script.aculo.us in a project that I’m working on. I was actually pretty impressed with it’s cross-browser compatibility and ease of use. I didn’t even think that the 200K size (including the Prototype Framework) was that bad. However, I did run into some issues while trying to make my site function properly for users that have JavaScript disabled, while still allowing the effects for those that do.

In my particular situation, I wanted to make some objects appear with an effect. You will notice that if you apply an Appear effect to an element, it will display, then disappear, the appear with the effect. The solution is to set an inline style with display:none, except that this will cause the element to be missing from users without JavaScript. The solution? Add the display:none style with Javascript.

First, I created a CSS class called effect_appear, with no styles.

.effect_appear {
}

Then I use Javascript to modify that class, and set the display to none.

function getStyleClass (className) {
    if (document.styleSheets.length < 1) {
        return null;
    }
    if (document.styleSheets[0].cssRules) {
        var cssRules = 'cssRules';
    } else {
        var cssRules = 'rules';
    }
    for (var s = 0; s < document.styleSheets.length; s++) {
        for (var r = 0; r < document.styleSheets[s][cssRules].length; r++) {
            if (document.styleSheets[s][cssRules][r].selectorText == '.' + className) {
                return document.styleSheets[s][cssRules][r];
            }
        }
    }
    return null;
}
getStyleClass('effect_appear').style.display = 'none';

Now, any element with that class will display normal to a user without JavaScript support, but will be missing for users with JavaScript. Next I added a ‘load’ event handler to the window element, which will make the elements appear with an effect.

Event.observe(window, 'load', effects);
function effects() {
    document.getElementsByClassName('effect_appear').each(
        function (el) {
            el.visualEffect('Appear');
        }
    );
}

However, there is still one problem. Because the style we created is not inline, it seems to override any effect that we add. The solution is to first remove the classname, and hide() the element (this will remove the class, but keep the element hidden, without the user ever seeing the element). Then we apply the visual effect (in this case, Appear).

Event.observe(window, 'load', effects);
function effects() {
    document.getElementsByClassName('effect_appear').each(
        function (el) {
            el.removeClassName('effect_appear').hide().visualEffect('Appear');
        }
    );
}

Here is the whole thing. Remember, the class “effect_appear” MUST exist in one of your stylesheets, or in a style tag ABOVE this code.

function getStyleClass (className) {
    if (document.styleSheets.length < 1) {
        return null;
    }
    if (document.styleSheets[0].cssRules) {
        var cssRules = 'cssRules';
    } else {
        var cssRules = 'rules';
    }
    for (var s = 0; s < document.styleSheets.length; s++) {
        for (var r = 0; r < document.styleSheets[s][cssRules].length; r++) {
            if (document.styleSheets[s][cssRules][r].selectorText == '.' + className) {
                return document.styleSheets[s][cssRules][r];
            }
        }
    }
    return null;
}
getStyleClass('effect_appear').style.display = 'none';
Event.observe(window, 'load', effects);
function effects() {
    document.getElementsByClassName('effect_appear').each(
        function (el) {
            el.removeClassName('effect_appear').hide().visualEffect('Appear');
        }
    );
}

About Aaron D. Campbell

Owner and lead developer at BlueDog, Aaron has 10+ years of web development experience, it a regular core contributor to the WordPress project, and has released many WordPress plugins.
This entry was posted in JavaScript, PrototypeJS, Script.aculo.us and tagged , , . Bookmark the permalink.

20 thoughts on “Properly degrading JavaScript Effects with Script.aculo.us

  1. christopher says:

    This is certainly the simplest way of achieving degradable Script.aculo.us effects that require the style display:none; I’ve come accross so far! Excellent snippet of code, and thanks for sharing :)

  2. Thank you. Hopefully there will be more on the way when I get some free time to blog again.

  3. daniel says:

    Hi, thanks so much! This is fantastic.

    I just changed your script a little little bit by deleting the last line of script “.visualEffect(‘Appear’) so that I could toggle effects (blind down/blind up) after the page has loaded. The script below works fine in IE7 and Firefox.

    However in Safari for some reason the script works the first time, then on reload it doesn’t hide the divs. Is there something wrong with my edited script? Or is this just a Safari problem?

    function getStyleClass (className) {
    if (document.styleSheets.length

  4. daniel says:

    hmm. your textbox is too small… ok so here’s the script once again, just where I changed it…

    Event.observe(window, ‘load’, effects);
    function effects() {
    document.getElementsByClassName(‘effect_appear’).each(
    function (el) {
    el.removeClassName(‘effect_appear’).hide();
    }
    );
    }

    cheers

  5. kyle says:

    I’m currently using scriptaculous with fastinit on my splashpage and am getting a delay before this script kicks in (there’s a form that appears and then disappears). Oddly enough, it seems to only effect one div, and the others which have the effect_appear class work perfectly. Any thoughts?

  6. I’m not sure. I’ve never used fastinit.

  7. kyle says:

    Just to follow up, I use Mint and had Mint’s script linked just before the closing body tag (at one point it was conflicting with an older script and it helped to move it there). I moved it back to just before the closing head tag and the delay seems to have dissappeared. Thanks for the nice work!

  8. Hey,
    I love what you’e doing!
    Don’t ever change and best of luck.

    Raymon W.

  9. RandyJones says:

    Looks Like Dallas is in trouble!
    Phoenix might end up blowing them all away.

    PHX vs. Det. Hmmm..Could be interesting?

  10. RandyJones: Your comments seems to have no mal-intent, but it also seems to have nothing to do with the article. Since your link gave a 404 error, I removed it.

  11. MaryAnne says:

    Hello,
    I like this place! You’ve done a great job.
    I’ll be back soon for sure. Take care

  12. MaryAnne: Link removed for similar reasons to above.

  13. planner says:

    Great piece of code. I can sure use this. However, I have a slightly related scriptaculous effect that I’m trying to implement, but it just won’t work. I’m hoping xavisys or any js guru reading this can help.
    Here’s what I’m trying to do:
    (This does not work)

    function scroll() {
    //Scope: books
    new Effect.Fade(‘six’, {queue: {duration: 3.0, scope: ‘books’} });
    new Effect.SlideDown(‘seven’, {queue: {position:‘end’, scope: ‘books’} });
    new Effect.Fade(‘seven’, {queue: {position: ‘end’, scope: ‘books’} });
    new Effect.BlindDown(‘six’, {queue: {position: ‘end’, scope: ‘books’} });
    //Scope: center
    new Effect.Fade(‘nine’, {queue: {duration: 3.0, scope: ‘center’} });
    new Effect.Appear(‘ten’, {queue: {position:‘end’, scope: ‘center’} });
    new Effect.Fade(‘ten’, {queue: {position: ‘end’, scope: ‘center’} });
    new Effect.Appear(‘eleven’, {queue: {position:‘end’, scope: ‘center’} });
    new Effect.Fade(‘eleven’, {queue: {position: ‘end’, scope: ‘center’} });
    new Effect.Appear(‘nine’, {queue: {position: ‘end’, scope: ‘center’} });
    new PeriodicalExecuter(scroll, 30);

    Here’s a simpler version that works:

    function scroll() {
    new Effect.Fade(‘six’, { duration: 3.0 });
    new Effect.SlideDown(‘seven’, { queue: ‘end’, duration: 6.0 });
    new Effect.Fade(‘seven’, { queue: ‘end’, duration: 6.0 });
    new Effect.BlindDown(‘six’, { queue: ‘end’, duration: 6.0 });
    }
    new PeriodicalExecuter(scroll, 30);

    I have zero js coding skill. Will appreciate any help fine tuning the first one.

    TIA

  14. It would help for you to describe what you WANT to happen, and what DOES happen. However, in the simple example, you are passing options like this:
    { queue: ‘end’, duration: 6.0 }
    Which adds it to the end of the effects queue, and then makes the effect take 6 seconds to complete. In your top example, you pass options like this:
    {queue: {position:‘end’, scope: ‘books’} }
    Which is invalid. The queue parameter needs to be numerical, and you are passing it a hash (contained in {}) Try something like this:
    {queue: ‘end’, scope: ‘books’}

  15. planner says:

    thanks for your response. Here’s what I’m trying to do:
    I have 2 groups of divs, each group in a separate box. The first div in each group is what is visible when the page loads, and the others have ‘display:none’ set. What I want is for the first div to fade out , say, in 6 or 3 seconds, for the next one to appear and then fade out just like the first one, and then the next and so on. Obviously, I’m going to have 2 scopes.
    What’s eating me is the syntax of having queue, scope, and duration in one effect.

    Hope my explanation makes sense.

    TIA

  16. I see what you want to do now. However, I still think it’s a syntax problem. Try this:
    {queue:{QUEUE OPTIONS ONLY}, other options}
    So:
    {queue: {duration: 3.0, scope: ‘books’} }
    becomes:
    {queue: {scope: ‘books’}, duration: 3.0}

  17. Raa says:

    I’m using getStyleClass to hide elements on my page but when I print my page, all the elements are displayed. Is their a way around that?

  18. Anonymous says:

    dude , today everyone has to have js enabled, and people who know nothing about web wont know and never wonder about to disable this thingy called js

  19. terry says:

    hi , nice code, but i dont understand because it is full of trash like &lt;

  20. Anonymous: Statistically speaking, there are plenty of people without JS. Some are on mobile browsers that don;t support it, some have it turned off, etc.

    terry: Please keep the language clean. It looks like somewhere during a WordPress upgrade, some characters got replaced by their entities. I fixed it. Thank you for pointing it out.

Leave a Reply

Your email address will not be published. Required fields are marked *

Note: If you are replying to another commenter, click the "Reply to {NAME} ↵" button under their comment!