Use Different CSS When Javascript Is Enabled

Legacy Content Notice: This post from 2012 demonstrates basic JavaScript detection patterns. While the core concept remains valid, modern approaches include CSS feature queries, intersection observers, and improved progressive enhancement strategies. For comprehensive modern JavaScript development practices, see our Modern JavaScript Development Guide.

Here is a snippet that allows you to have different CSS for when Javascript is turned on or off. You may need different CSS so you can deal with visitors who have Javascript turned off, you may want to display things differently. To change how you display different CSS for when Javascript is on you can add a CSS class to the html tag so you can filter the CSS down to the child tags.

Javascript

Here is a raw Javascript way to add a CSS class if Javascript is turned on.



document.documentElement.className = "js";


This will change the HTML tag to



<html class="js">


jQuery

If you use jQuery then you can just use the following which will do the same thing.



$(document).ready(function(){
     $("html").addClass("js");
});


Change The CSS

Now you have a CSS class on the HTML tag you can change the child elements.



.slides { display:block; }
.js .slides { display:none; }


This will change the slides element to hidden if Javascript is turned on which means you can add the functionality of the slideshow with the Javascript.

Modern Approaches (2024)

Method 1: Enhanced JavaScript Detection

// Modern JavaScript detection with feature support
class JavaScriptDetector {
    constructor() {
        this.detectFeatures();
    }

    detectFeatures() {
        const html = document.documentElement;

        // Basic JS detection
        html.classList.add("js");
        html.classList.remove("no-js");

        // Feature detection
        const features = {
            "supports-grid": CSS.supports("display", "grid"),
            "supports-flexbox": CSS.supports("display", "flex"),
            "supports-custom-properties": CSS.supports(
                "--custom-property",
                "value"
            ),
            "supports-intersection-observer": "IntersectionObserver" in window,
            "supports-resize-observer": "ResizeObserver" in window,
            "supports-local-storage": this.supportsLocalStorage(),
            "supports-webp": this.supportsWebP(),
            "reduced-motion": window.matchMedia(
                "(prefers-reduced-motion: reduce)"
            ).matches,
        };

        // Add feature classes
        Object.entries(features).forEach(([feature, supported]) => {
            html.classList.toggle(feature, supported);
            html.classList.toggle(`no-${feature}`, !supported);
        });

        // Device type detection
        this.detectDeviceType();
    }

    supportsLocalStorage() {
        try {
            const test = "test";
            localStorage.setItem(test, test);
            localStorage.removeItem(test);
            return true;
        } catch {
            return false;
        }
    }

    async supportsWebP() {
        if (!window.createImageBitmap) return false;

        const webpData =
            "data:image/webp;base64,UklGRiQAAABXRUJQVlA4IBgAAAAwAQCdASoBAAEAAwA0JaQAA3AA/vuUAAA=";
        const blob = await fetch(webpData).then((r) => r.blob());

        try {
            await createImageBitmap(blob);
            return true;
        } catch {
            return false;
        }
    }

    detectDeviceType() {
        const html = document.documentElement;
        const userAgent = navigator.userAgent.toLowerCase();

        if (/mobile|android|iphone|ipad|phone/i.test(userAgent)) {
            html.classList.add("mobile-device");
        } else {
            html.classList.add("desktop-device");
        }

        // Touch device detection
        if ("ontouchstart" in window || navigator.maxTouchPoints > 0) {
            html.classList.add("touch-device");
        } else {
            html.classList.add("no-touch");
        }
    }
}

// Initialize when DOM is ready
if (document.readyState === "loading") {
    document.addEventListener(
        "DOMContentLoaded",
        () => new JavaScriptDetector()
    );
} else {
    new JavaScriptDetector();
}

Method 2: CSS-First Progressive Enhancement

<!-- In your HTML head -->
<script>
    // Immediate script to prevent FOUC (Flash of Unstyled Content)
    document.documentElement.classList.remove("no-js");
    document.documentElement.classList.add("js");
</script>
/* Default styles (no JavaScript) */
.slideshow {
    display: block;
}

.slideshow .slide:not(:first-child) {
    display: none;
}

/* JavaScript enhanced styles */
.js .slideshow {
    position: relative;
    overflow: hidden;
}

.js .slideshow .slide {
    display: block;
    position: absolute;
    top: 0;
    left: 0;
    opacity: 0;
    transition: opacity 0.5s ease-in-out;
}

.js .slideshow .slide.active {
    opacity: 1;
}

/* Feature-specific styles */
.supports-grid .layout {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
}

.no-supports-grid .layout {
    display: flex;
    flex-wrap: wrap;
}

/* Reduced motion preference */
.reduced-motion .slideshow .slide {
    transition: none;
}

/* Device-specific styles */
.mobile-device .navigation {
    font-size: 1.2em;
    padding: 1em;
}

.desktop-device .navigation {
    font-size: 1em;
    padding: 0.5em;
}

Method 3: Modern Component-Based Approach

// Modern component that adapts based on JavaScript availability
class ProgressiveSlideshow {
    constructor(element) {
        this.element = element;
        this.slides = [...element.querySelectorAll(".slide")];
        this.currentIndex = 0;

        if (this.slides.length > 1) {
            this.enhance();
        }
    }

    enhance() {
        // Add JavaScript-specific structure
        this.element.classList.add("js-enhanced");

        // Create navigation if it doesn't exist
        if (!this.element.querySelector(".slideshow-nav")) {
            this.createNavigation();
        }

        // Set up automatic rotation with pause on hover
        this.setupAutoRotation();

        // Handle reduced motion preference
        this.handleReducedMotion();

        // Initialize first slide
        this.showSlide(0);
    }

    createNavigation() {
        const nav = document.createElement("div");
        nav.className = "slideshow-nav";

        const prevBtn = document.createElement("button");
        prevBtn.textContent = "‹";
        prevBtn.className = "slide-btn slide-prev";
        prevBtn.addEventListener("click", () => this.prevSlide());

        const nextBtn = document.createElement("button");
        nextBtn.textContent = "›";
        nextBtn.className = "slide-btn slide-next";
        nextBtn.addEventListener("click", () => this.nextSlide());

        nav.append(prevBtn, nextBtn);
        this.element.appendChild(nav);
    }

    setupAutoRotation() {
        if (window.matchMedia("(prefers-reduced-motion: reduce)").matches) {
            return; // Respect user's motion preferences
        }

        this.autoRotateInterval = setInterval(() => {
            this.nextSlide();
        }, 5000);

        // Pause on hover
        this.element.addEventListener("mouseenter", () => {
            clearInterval(this.autoRotateInterval);
        });

        this.element.addEventListener("mouseleave", () => {
            this.setupAutoRotation();
        });
    }

    handleReducedMotion() {
        const prefersReducedMotion = window.matchMedia(
            "(prefers-reduced-motion: reduce)"
        );

        if (prefersReducedMotion.matches) {
            this.element.classList.add("reduced-motion");
        }
    }

    showSlide(index) {
        this.slides.forEach((slide, i) => {
            slide.classList.toggle("active", i === index);
        });
        this.currentIndex = index;
    }

    nextSlide() {
        const nextIndex = (this.currentIndex + 1) % this.slides.length;
        this.showSlide(nextIndex);
    }

    prevSlide() {
        const prevIndex =
            (this.currentIndex - 1 + this.slides.length) % this.slides.length;
        this.showSlide(prevIndex);
    }
}

// Auto-initialize all slideshows
document.addEventListener("DOMContentLoaded", () => {
    document.querySelectorAll(".slideshow").forEach((slideshow) => {
        new ProgressiveSlideshow(slideshow);
    });
});

Legacy Approaches (Historical Reference)

Javascript

Here is a raw Javascript way to add a CSS class if Javascript is turned on.

document.documentElement.className = "js";

This will change the HTML tag to

<html class="js"></html>

jQuery

If you use jQuery then you can just use the following which will do the same thing.

$(document).ready(function () {
    $("html").addClass("js");
});

Change The CSS

Now you have a CSS class on the HTML tag you can change the child elements.

.slides {
    display: block;
}
.js .slides {
    display: none;
}

Key Modern Advantages

  • Feature Detection: Check for specific capabilities, not just JavaScript
  • Progressive Enhancement: Start with basic functionality, enhance with JavaScript
  • Accessibility: Respect user preferences like reduced motion
  • Performance: Avoid FOUC and optimize for perceived performance
  • Maintainability: Component-based approach for complex interactions