« Back to Index

detect CSS pseudo-element selector support

View original Gist on GitHub

Tags: #js

detect.css

body {
    font: normal small Helvetica, Arial, Verdana, sans-serif;
}

h1 {
    border-bottom: 1px solid #C00;
    color: #666;
    font-weight: bold;
    margin-bottom: 10px;
}

div {
    border: 1px dashed #333;
    padding: 10px;
}

div,
div p {
    margin-bottom: 10px;    
}

code {
    color: blue;
}

ul {
    list-style: disc;
    margin-left: 10px;
    padding-left: 10px;
}

.before #apply:before {
    color: red;
    content: "before";
    display: inline-block;
    margin: 0 5px 5px 0;
}

.after #apply:after {
    color: green;
    content: "after";
    display: inline-block;
    margin: 0 0 0 55px;
}

detect.html

<h1>Detection script for CSS :before and :after support</h1>
<div>
    <p>This script detects support of the CSS selectors <code>:before</code> and <code>:after</code> by:</p>
    <ul>
        <li>Adding two empty paragraph elements to the page (effectively their width each is zero).</li>
        <li>Adding a style element to the page (+inserting the relevant <code>:before</code>/<code>:after</code> code).</li>
        <li>Checking the width of the element (we need to set them to <code>display:inline-block</code>).</li>
        <li>If the width is greater than zero then we know the content was added so support is detected.</li>
        <li>Much like <a href="http://www.modernizr.com/">Modernizr</a> we add relevant class names to the main <code>html</code> element</li>
        <li>Had to go back and make changes for Internet Explorer (see code comments)</li>
    </ul>
</div>
<p id="apply">Paragraph 1</p>
<p>Paragraph 2</p>

detect.js

// Get a style property (name) of a specific element (elem)
function getStyle(elem, name) {
    // If the property exists in style[], then it's been set recently (and is current)
    if (elem.style[name]) {
        return elem.style[name];
    }
    // Otherwise, try to use IE's method
    else if (elem.currentStyle) {
        return elem.currentStyle[name];
    }
    // Or the W3C's method, if it exists
    else if (document.defaultView && document.defaultView.getComputedStyle) {
        // It uses the traditional 'text-align' style of rule writing, instead of textAlign
        name = name.replace(/([A-Z])/g, '-$1');
        name = name.toLowerCase();

        // Get the style object and get the value of the property (if it exists)
        var s = document.defaultView.getComputedStyle(elem, '');

        return s && s.getPropertyValue(name);
    }
    // Otherwise, we're using some other browser
    else {
        return null;
    }
}

window.onload = function() {
    var doc = document,
        html = doc.documentElement,
        body = doc.body,
        paraBefore = doc.createElement('p'),
        paraAfter = doc.createElement('p'),
        styleBefore = doc.createElement('style'),
        styleAfter = doc.createElement('style'),
        widthBefore, widthAfter, selectorsBefore = '#testbefore { display:inline-block; } #testbefore:before { content: "before"; }',
        selectorsAfter = '#testafter { display:inline-block; } #testafter:after { content: "after"; }';

    // Internet Explorer seems to need a type attribute to recognise a stylesheet?
    styleBefore.type = 'text\/css';
    styleAfter.type = 'text\/css';

    paraBefore.id = 'testbefore';
    paraAfter.id = 'testafter';

    // For Internet Explorer...
    if (styleBefore.styleSheet) {
        styleBefore.styleSheet.cssText = selectorsBefore;
        styleAfter.styleSheet.cssText = selectorsAfter;
    } else {
        styleBefore.appendChild(doc.createTextNode(selectorsBefore));
        styleAfter.appendChild(doc.createTextNode(selectorsAfter));
    }

    body.appendChild(styleBefore);
    body.appendChild(styleAfter);
    body.appendChild(paraBefore);
    body.appendChild(paraAfter);

    // Internet Explorer doesn't always play ball…
    widthBefore = (getStyle(document.getElementById('testbefore'), 'width') === 'auto') ? document.getElementById('testbefore').offsetWidth : parseInt(getStyle(document.getElementById('testbefore'), 'width'));
    widthAfter = (getStyle(document.getElementById('testafter'), 'width') === 'auto') ? document.getElementById('testafter').offsetWidth : parseInt(getStyle(document.getElementById('testafter'), 'width'));

    if (widthBefore > 0) {
        html.className += ' before';
        body.removeChild(styleBefore);
        body.removeChild(paraBefore);
    } else {
        console.log('before failed');
    }

    if (widthAfter > 0) {
        html.className += ' after';
        body.removeChild(styleAfter);
        body.removeChild(paraAfter);
    } else {
        console.log('after failed');
    }
};