Category: "JavaScript"

CSS list filter and view

The goal of this fiddle was to provide a CSS and JavaScript method to switch between a list and card or grid view.

It uses jQuery and a very simple filter that fires on keyup.

The code is commented here, you can see it run at http://jsfiddle.net/PbCV3/5/


$(document).ready(function () {

    $("#filter").keyup(function () {
        var filter = $(this).val().toLowerCase();
        var len = filter.length;
        $("li").each(function () {
            /* You may want to use indexOf instead of substring to filter */ 
            if ($(this).html().substring(0, len).toLowerCase() != filter) {
                $(this).addClass('hidden');
            } else {
                $(this).removeClass('hidden');
            }
        });
        /* Check if the list is in card view or list view */
        if ($("ul").hasClass("cards")) {
            cards();
        } else {
            list();
        }
    });
    $("#controls").delegate("button", "click", function () {
        /* The id of the button clicked corresponds to the class of the list */
        var id = $(this).attr("id");
        $("ul").removeClass();
        $("ul").addClass(id);
        if (id == "cards") {
            cards();
        } else {
            list();
        }
    });

    function cards() {
        var count = 0,
            adjacent, adjHeight, thisHeight, newHeight;
        /* The visible pseudo class is a jQuery extension */
        $("li:visible").each(function () {
            /* adjacent is the item of the list which is on the same row */
            adjacent = (count % 2) ? $(this).prev("li:visible") : $(this).next("li:visible");
            if (adjacent) {
                adjHeight = $(adjacent).height();
                thisHeight = $(this).height();
                /* The new height should be the height of the taller item */ 
                newHeight = Math.max(adjHeight, thisHeight);
                if (newHeight == thisHeight) {
                    $(adjacent).height(newHeight);
                } else {
                    $(this).height(newHeight);
                }
            }
            /* count is used to determine whether the item is on the left or right */
            count++;
        });
    }

    function list() {
        /* Restore the heights to auto */
        $("li").each(function () {
            $(this).height("auto");
        });
    }

});

dgrid Print Shim

If you need to print a dgrid, you can query the store and create an HTML table with JavaScript.

Create and work with the dgrid as usual, but add a second div below it which will contain the table.


<div class="no-print" id="grid"></div>
<div class="print-only" id="grid_print"></div>

Then set up the CSS to hide the print-only by default. Create a print.css file and the appropriate media attribute hide the no-print class and display the print-only.

Relevant lines of print.css

.print-only {
        display: block;
}
.print-only table {
        width: 100%;
        table-layout: fixed;
}
.print-only table th {
        border: 1px solid #000;
        font-weight: bolder;
}

Relevant lines of admin.css (screen)


.print-only,.page-break {
        display:none;
}

Link tag for the print.css file

<link media="print” href=’/css/print.css’ rel=’stylesheet’ type=’text/css’>

JavaScript to create the table. This code is used on a page with several grids, the grids are in the grids object, with stores in the stores object.


                byId("print_button").onclick = function() {
                                /* This will find all the print_grids */
                                query("[id$='_grid_print']").forEach(function(node) {
                                                if (node.hasChildNodes()) {
                                                        node.removeChild(node.firstChild);
                                                }

                                                var grid, grid_id;

                                                grid_id = node.id.replace(/_grid_print/,'');
                                                grid = grids[grid_id];

                                                var store = stores[grid_id];
                                                var data = store.query({});

                                                if (data.length === 0) {
                                                        var no_data = document.createTextNode("None at this time");
                                                        node.appendChild(no_data);
                                                } else {
                                                        var table, tr, th, td, content, c;
                                                        var hasNumber = false;
                                                        table = document.createElement("table");
                                                        tr = document.createElement("tr");
                                                        for (c in grid.columns) {
                                                                content = document.createTextNode(grid.columns[c].label);
                                                                th = document.createElement("th");
                                                                th.appendChild(content);
                                                                tr.appendChild(th);
                                                                if (c === "number") hasNumber = true;
                                                        }
                                                        table.appendChild(tr);

                                                        if (hasNumber) {
                                                                data = store.query({},[{attribute:"number"}]);
                                                        }
                                                        var i = 1,j,l;
                                                        l = data.length;
                                                        for (j=0; j<l; j++) {
                                                                tr = document.createElement("tr");
                                                                for (c in grid.columns) {
                                                                        content = document.createTextNode(data[j][c]);
                                                                        td = document.createElement("td");
                                                                        td.appendChild(content);
                                                                        tr.appendChild(td);
                                                                }
                                                                table.appendChild(tr);
                                                                i++;
                                                        }
                                                        node.appendChild(table);
                                                }
                                        });
                                window.print()
                        };

The hasNumber code is used to order the rows by a number in one of the columns.

This post courtesy of Worktrainer.

HTML5 input tag required and placeholder shim for IE

Two of the nice HTML5 attributes for input tags are required and placeholder.

http://www.w3schools.com/html5/att_input_required.asp
http://www.w3schools.com/tags/att_input_placeholder.asp

Unfortunately, IE doesn’t support them. This code snippet allows a the required and placeholder attributes to act the same under IE as they do with browsers that support them. It also allows styling of disabled inputs and textareas under IE.

The only other code is some CSS that defines a class named “required” which is used to highlight the empty inputs.


.invalid {
        color: #800;
}
.required {
        border: 1px solid #800 !important;
}
.ie [placeholder] {
        color: #aaa;
}
.ie [placeholder]:focus {
        color: #000;
}
.ie [disabled]{
        color: #333;
        background-color: #efefef;
}


document.body.className += " ie";

if(!String.prototype.trim) {
        String.prototype.trim = function () {
                return this.replace(/^\s+|\s+$/,'');
        };
}

var form = document.querySelector('form');

if (form !== null) {
        var disabled_inputs = document.querySelectorAll("[disabled]");
        var required_inputs = document.querySelectorAll("[required]");
        var placeholders = document.querySelectorAll("[placeholder]");

        clearPlaceholder = function(evt) {
                var element;
                if (evt.target) {
                        evt.target.value = "";
                        element = document.getElementById(evt.target.id);
                } else if (evt.srcElement) {
                                evt.srcElement.value = "";
                                element = evt.srcElement;
                        } else {
                                return;
                        }
                element.className = element.className.replace(" required", "");
                if (element.removeEventListener) {
                        element.removeEventListener('focus', clearPlaceholder);
                } else if (element.detachEvent) {
                        element.detachEvent('onfocus', clearPlaceholder);
                }
        }

        var e;
        var l = placeholders.length;
        for (var p = 0; p < l; p++) {
                if (typeof placeholders[p].id !== undefined) {
                        e = document.getElementById(placeholders[p].id);
                        if (e !== null) {
                                e.value = e.getAttribute("placeholder");
                                if (e.addEventListener) {
                                        e.addEventListener('focus', clearPlaceholder);
                                } else if (e.attachEvent) {
                                        e.attachEvent('onfocus',clearPlaceholder);
                                }
                        }
                }
        }


        var e;
        var l = disabled_inputs.length;
        for (var d = 0; d < l; d++) {
                if (typeof disabled_inputs[d].id !== undefined) {
                        e = document.getElementById(disabled_inputs[d].id);
                        if (e !== null) {
                                e.removeAttribute("disabled");
                                e.className += " disabled";
                        }
                }
        }

        form.onsubmit = function() {
                var completed = true;
                var value = "";
                var inputs = document.querySelectorAll("input, textarea");
                var e, l = inputs.length;
                var req = /required/;
                var disabled = /disabled/;
                for (var i = 0; i < l; i++) {
                        e = document.getElementById(inputs[i].id);
                        if (e.hasAttribute("required")) {
                                if (!e.hasAttribute("placeholder")) {
                                        e.setAttribute("placeholder", "");
                                }
                                e.value = e.value.trim();
                                if ((e.value === "") || (e.value === e.getAttribute("placeholder"))) {
                                        e.value = e.getAttribute("placeholder");
                                        if (!req.test(e.className)) {
                                                e.className += " required";
                                                if (e.addEventListener) {
                                                        e.addEventListener('focus', clearPlaceholder);
                                                } else if (e.attachEvent) {
                                                        e.attachEvent('onfocus',clearPlaceholder);
                                                }
                                        }
                                        completed = false;
                                }
                        }
                        if (disabled.test(e.className)) {
                                e.setAttribute("disabled");
                                e.className = e.className.replace(" disabled", "");
                        }
                }
                return completed;
        }
}

Browser Cache Management - Ensure Updates

This post describes one way to ensure the javascript and CSS are requested by the browser when a new release is distributed.

The first approach is to prefix the file names with a version-release string, and create symlinks to the files during installation or the first execution. Many systems have a version identification mechanism.

To manage the symlinks, the following could be used:


#!/bin/bash

fclearold()
{
        echo 'fclearold'
        for g in $(find *.$1 -maxdepth 1 -type l);do
                echo $g
                rm -f $g
        done
}

fcreatenew()
{
        echo 'fcreatenew'
        for g in $(ls *.$1); do
                ln -s $g $2.$g
        done

}

version=`cat "$BASE/config/version`;
for f in 'js' 'css'; do
        echo $f
        pushd $f > /dev/null
        fclearold $f
        echo $version;
        fcreatenew $f $version
        popd > /dev/null
done

fclearold removes the old symlinks, fcreatenew makes new ones. It is assumed the javascript is in the js directory and all javascript files have a .js extension, and the CSS is in the css directory and all CSS files have a .css extension.

httpd.conf (or equivalent)

# Cache js and CSS files for 6 months - with a timestamp
<FilesMatch "\.(js|css)$">
  ExpiresActive On
  ExpiresDefault "access plus 6 months"
  Header set Cache-Control "max-age=15552000"
  RewriteEngine On
  RewriteRule (.*)/[0-9]+\.(.*)$ $1/$2
  FileETag MTime
</FilesMatch>

Timestamp Management Code (PHP)


        function sTimestamp()
        {
                $sTimestampFile = 'cache/timestamp';
                if (is_file($sTimestampFile))
                        $sRetVal = file_get_contents($sTimestampFile);
                else
                {
                        $sRetVal = time();
                        file_put_contents($sTimestampFile,$sRetVal);
                }
                return $sRetVal;
        }

Once the timestamp is set, it is cached in the cache/timestamp file. In this system, the cache is cleared when new releases are installed, so the absence of the timestamp file ensures an update and a new set of requested files.

The timestamp can applied to .js and .css file requests like so:

<script type="text/javascript" src="js/<?php echo $timestamp ?>.code.js"></script>

Great New Web Resource

CoderZone.org launched recently.

It’s great new resource for web people, from ‘n00bs’ to ‘w00ts’. What makes it special:

  • A great team of moderators. These guys are experienced and know the web.
  • A library of code snippets, little bits of code that will save you a tremendous amount of time. You can contribute code, too.
  • XHTML/HTML & CSS sandboxes so you can test out ideas quickly.
  • An SQL sandbox for testing queries.
  • It’s free.
  • A very cool design.
  • No ads, the forum is there to help people, not distract you with ads you aren’t going to click on anyway.