« Back to Index

JavaScript grid overlay

View original Gist on GitHub

Grid-Overlay.js

(function(){
    var boundaryInserted, grid12, grid24, boundary, firstTimeVisible, magicNumber = 126;

    function getDocHeight(){
        var body = document.body,
            elem = document.documentElement;

        return Math.max(
            Math.max(body.scrollHeight, elem.scrollHeight),
            Math.max(body.offsetHeight, elem.offsetHeight),
            Math.max(body.clientHeight, elem.clientHeight)
        );
    }

    function insertBoundary(){
        if (!boundaryInserted) {
            boundaryInserted = true;

            var styles = '<style>\
                        .boundary { background-color: #CCC; color: black; margin: auto; max-width: 1028px; padding-bottom: .5em; padding-top: .5em; position: relative; text-align: center; z-index: 9999; }\
                        .boundary__border { background-color: #CCC; position: absolute; top: 0; width: 1px; }\
                        .boundary__border--left { left: 0; }\
                        .boundary__border--right { right: 0; }\
                        </style>';

            var overlay = document.createElement('div');
                overlay.id = 'js-overlay';

            var overlay24 = document.createElement('div');
                overlay24.id = 'js-overlay-24';

            var boundary = document.createElement('div');
                boundary.className = 'boundary';
                boundary.innerHTML = 'This element indicates the outer margins (which *used* to be incorporated into the Grid, but not any more)<br><br><b>\
                                        Press:<br>\
                                        "a" to toggle everything<br>\
                                        "b" to toggle boundary box<br>\
                                        "g" key to toggle 12 column grid visibility<br>\
                                        "f" key to toggle 24 column grid visibility</b>';

            var wrapper = document.querySelector('.wrapper');
                wrapper.parentNode.insertBefore(boundary, wrapper);

            var borderLeft = document.createElement('div');
                borderLeft.className = 'boundary__border boundary__border--left';

            var borderRight = document.createElement('div');
                borderRight.className = 'boundary__border boundary__border--right';

            boundary.appendChild(borderLeft);
            boundary.appendChild(borderRight);
            document.body.appendChild(overlay);
            document.body.appendChild(overlay24);

            $(boundary).before($(styles));
        }
    }

    function setUpKeyBindings(){
        var visible = false;

        $(document).on('keyup', function (e) {
            // Toggle 12 column grid (g)
            if (e.keyCode === 71) {
                grid12.toggle();
            }

            // Toggle 24 column grid (f)
            if (e.keyCode === 70) {
                grid24.toggle();
            }

            // Toggle outer boundary (b)
            if (e.keyCode === 66) {
                boundary.toggle();
            }

            // Toggle everything (a)
            if (e.keyCode === 65) {
                if (visible) {
                    boundary.hide();
                    grid12.hide();
                    grid24.hide();
                    visible = false;
                } else {
                    // If it's the first time we're displaying the boundary and grids then ensure their height is set
                    if (!firstTimeVisible) {
                        $('.boundary__border, #gridpak, #gridpak24').each($.proxy(function (index, item) {
                            item.style.height = getDocHeight() + magicNumber + 'px';
                        }, this));

                        firstTimeVisible = true;
                    }

                    boundary.show();
                    grid12.show();
                    grid24.show();
                    visible = true;
                }
            }
        });
    }

    function Gridpak (numberOfColumns, userProvidedGrids) {
        this.$container = {};
        this.append = (numberOfColumns === 24) ? '#js-overlay-24' : '#js-overlay'; // DOM element to append the Gridpak too
        this.columnNumber = numberOfColumns;
        this.injectCSSName = (numberOfColumns === 24) ? 'gridpak24' : 'gridpak';
        this.hideOnSmallerScreens = (numberOfColumns === 24) ? true : false;
        this.css = '';
        this.userProvidedGrids = userProvidedGrids;
        this.gridpakWrapper = '';
        this.numberOfColumns = numberOfColumns;
        this.init();
    }

    Gridpak.prototype.init = function(){
        insertBoundary();

        var grids = this.userProvidedGrids,
            numGrids = grids.length - 1,
            i = 0;

        this.css += '<style type="text/css"> ' +
            '#' + this.injectCSSName + ' { ' +
                'width:100%; ' +
                'height:100%; ' +
                'display:block; ' +
                'position:absolute; ' +
                'top:0; ' +
                'left:0; ' +
                'z-index:9998; ' +
            '} ' +
            '#' + this.injectCSSName + ' .' + this.injectCSSName + '_wrapper { ' +
                'max-width:996px; ' +
                'height:100%; ' +
                'margin:auto; ' +
                'padding-left:16px; ' +
                'padding-right:16px; ' +
            '} ' +
            '#' + this.injectCSSName + ' .' + this.injectCSSName + '_wrapper.' + this.injectCSSName + '_wrapper--smallerOuterMargins { ' +
                'padding-left:8px; ' +
                'padding-right:8px; ' +
            '} ' +
            '#' + this.injectCSSName + ' .' + this.injectCSSName + '_grid { ' +
                'height:100%; ' +
                'display:none; ' +
            '} ' +
            '#' + this.injectCSSName + ' .' + this.injectCSSName + '_col { ' +
                'border-left:0 solid rgba(255,255,255,0); ' +
                'border-right:0 solid rgba(255,255,255,0); ' +
                '-moz-background-clip: padding; -webkit-background-clip: padding-box; background-clip: padding-box;' +
                'padding:0; ' +
                '-webkit-box-sizing:border-box; -moz-box-sizing:border-box; box-sizing:border-box; ' +
                'display:block; ' +
                'float:left; ' +
                'height:100%; ' +
                'background-color:rgba(153,0,0,0.2); ' +

            '} ' +
            '#' + this.injectCSSName + ' .' + this.injectCSSName + '_visible { ' +
                'width:100%; ' +
                'height:100%; ' +
                'display:block; ' +
                'background:rgba(255,255,255,0.3); ' +
            '} ';

        this.markup = '<div id="' + this.injectCSSName + '"><div class="' + this.injectCSSName + '_wrapper">';

        // Put the grids on the screen
        for (i; i<=numGrids; i++) {
            this.drawGrid(grids[i], i);
        }

        this.markup += '</div></div>';

        this.css += '</style>';

        if (this.columnNumber === 24) {
            grid24 = this.$container = $(this.markup);
        } else {
            grid12 = this.$container = $(this.markup);
        }

        this.topLevelContainerElement = $(this.append);

        this.topLevelContainerElement.prepend(this.css);

        this.topLevelContainerElement.append(this.$container);

        boundary = this.boundary = $('.boundary');

        this.gridpakWrapper = $('.' + this.injectCSSName + '_wrapper');

        this.checkOuterMargins();

        // Hide the boundary and the grids on load
        this.boundary.hide();
        this.$container.hide();

        $(window).on('resize', $.proxy(this.checkOuterMargins, this));
    };

    Gridpak.prototype.checkOuterMargins = function(){
        if (this.columnNumber === 24) {
            this.hideOnSmallerScreens = true;
        }

        if (document.documentElement.clientWidth < 600) {
            if (this.hideOnSmallerScreens) {
                this.topLevelContainerElement.hide();
            }
        } else {
            if (this.hideOnSmallerScreens) {
                this.topLevelContainerElement.show();
            }
        }

        if (document.documentElement.clientWidth < 400) {
            this.gridpakWrapper.addClass(this.injectCSSName + '_wrapper--smallerOuterMargins');
        } else {
            this.gridpakWrapper.removeClass(this.injectCSSName + '_wrapper--smallerOuterMargins');
        }

    };

    Gridpak.prototype.drawGrid = function (grid, num) {
        var new_markup = '',
            i = 1,
            gutter_pc = (grid.gutter_type === '%') ? grid.gutter_width : 0,
            gutter_px = (grid.gutter_type === 'px') ? grid.gutter_width : 0,
            width = 0;

        if (grid.gutter_type === 'px') {
            width = 100 / grid.col_num;
        } else {
            width = (100 - (gutter_pc * (grid.col_num - 1))) / grid.col_num;
        }

        new_markup = '<div class="' + this.injectCSSName + '_grid ' + this.injectCSSName + '_grid_' + num + '">';

        this.css += '#' + this.injectCSSName + ' .' + this.injectCSSName + '_grid_' + num + ' { ' +
            'margin-left:-' + gutter_px + 'px; ' +
        '} ' +
        '#' + this.injectCSSName + ' .' + this.injectCSSName + '_grid_' + num + ' .' + this.injectCSSName + '_col { ' +
            'width:' + width + '%; ' +
            'margin-left:' + gutter_pc + '%; ' +
            'border-left-width:' + gutter_px + 'px; ' +
            'padding-left:' + grid.padding_width + grid.padding_type +'; ' +
            'padding-right:' + grid.padding_width + grid.padding_type + '; ' +
        '} ';
        if (grid.gutter_type === '%') {
            this.css += '#' + this.injectCSSName + ' .' + this.injectCSSName + '_grid_' + num + ' .' + this.injectCSSName + '_col:first-child { ' +
                'margin-left:0;' +
            '} ';
        }

        this.css += '@media screen and (min-width: ' + grid.min_width + 'px) ';
        if (grid.upper !== false) {this.css += 'and (max-width: ' + grid.upper + 'px) ';}
        this.css += ' { ' +
            '#' + this.injectCSSName + ' .' + this.injectCSSName + '_grid_' + num + ' { ' +
                'display: block; ' +
            '} ' +
        '} ';

        for(i; i<=grid.col_num; i++) {

            new_markup += '<div class="' + this.injectCSSName + '_col"><div class="' + this.injectCSSName + '_visible"></div></div>';
        }

        new_markup += '</div>';

        this.markup += new_markup;
    };

    setUpKeyBindings();

    // 12 Column Grid
    var grid = new Gridpak(12, [
        {
            min_width: 0,
            col_num: 12,
            gutter_type: 'px',
            gutter_width: 8,
            padding_type: 'px',
            padding_width: 0,
            upper: 419
        },
        {
            min_width: 400,
            col_num: 12,
            gutter_type: 'px',
            gutter_width: 16,
            padding_type: 'px',
            padding_width: 0,
            upper: 599
        },
        {
            min_width: 600,
            col_num: 12,
            gutter_type: 'px',
            gutter_width: 16,
            padding_type: 'px',
            padding_width: 0,
            upper: 1000
        },
        {
            min_width: 1001,
            col_num: 12,
            gutter_type: 'px',
            gutter_width: 16,
            padding_type: 'px',
            padding_width: 0,
            upper: false
        }
    ]);

    // 24 Column Grid
    var grid_24 = new Gridpak(24, [
        {
            min_width: 600,
            col_num: 24,
            gutter_type: 'px',
            gutter_width: 16,
            padding_type: 'px',
            padding_width: 0,
            upper: 1000
        },
        {
            min_width: 1001,
            col_num: 24,
            gutter_type: 'px',
            gutter_width: 16,
            padding_type: 'px',
            padding_width: 0,
            upper: false
        }
    ]);

}());