Friday, October 31, 2008

A Simple, Flexible Architecture For Popup Warnings

I have looked around the web for a good way to display on screen warnings and found most examples wanting. They were rather bloaty, required the use of a different library or you had to pay for them. So I set out and with my favorite javascript library, jQuery, and made my own.

My goals were pretty simple:
  • I wanted to use built-in capabilities when I could (e.g. the tooltip/hover title/alt)
  • Use jQuery to speed up the development and make it easily cross-browser compatible
  • Make it small, compact, and as few lines as possible
  • Assume no needed html will be on the page calling the function
This is still a work in progress but it is completely functional in it's current state. In my CSS file:
.popup{
    border:1px solid #CCC;
 position:absolute;
 width:250px;
 border:1px solid #c93;
 background:#ffc;
 padding:5px;
 right:3px;
 top : -175px;
 font-weight:bold;
}
.popup div p
{
 font-weight: normal;
 padding: 3px;
 margin: 0px;
}
.nohref, .popup div p {
 cursor: pointer;
 cursor: hand;
 text-decoration: underline;
}
In my javascript file:
var getScrollXY = function() {
    var w=window, db=document.body, dde=document.documentElement;
    return ( typeof( w.pageYOffset ) == 'number' )          ? [w.pageXOffset, w.pageYOffset] :
            ( db && ( db.scrollLeft || db.scrollTop ) )     ? [db.scrollLeft, db.scrollTop] :
            ( dde && ( dde.scrollLeft || dde.scrollTop ) )  ? [dde.scrollLeft, dde.scrollTop] : [0, 0];
};
varClearCtlBg = function(ctl) {
    ctl.style.background = "#FFF";
    ctl.setAttribute('title', "");
};
var Validate = function (ctl, rule, msg, idsuffix) {
    if (rule && msg && (msg.length>0)) {
    var ctlid = ctl.id+idsuffix;
    ctl.style.background = "#FFF url(http://intellectualponderings.googlecode.com/svn/trunk/blog/images/invalid_line.gif) repeat-x scroll center bottom";
    cTitle = ctl.getAttribute('title');
    if (cTitle.indexOf(msg) == -1) {
        ctl.setAttribute('title', ((cTitle) ? cTitle + "\r\n[ " : "[ ") + msg);
    }
    var warning = "<p id=\"" + ctlid + "_Warn\" onclick=\"focusSelect('" + ctl.id + "');\">" + msg + "</p>";
    if ($("#WarnMsg").length === 0) {
        $("body").append("<div class=\"popup\" id=\"WarnMsg\"><a class=\"nohref\" style=\"float: right;\" onclick=\"$('#WarnMsg').fadeOut('slow').remove();\">X</a>Warning<div>"+ warning +"</div></div>");//
        $("#WarnMsg").show().animate({ top: String(Number(getScrollXY()[1])+3)+"px" }, 750 );//-175
        //$("#closeMessage").click(function() {$("#WarnMsg").fadeOut("slow").remove();}); id=\"closeMessage\" style=\"display: none;\"
    } else if ($("#"+ctlid+"_Warn").length > 0) {
        $("#"+ctlid+"_Warn").fadeOut(100).fadeIn(100).fadeOut(100).fadeIn(100).fadeOut(100).fadeIn(100);
    } else {
        $("#WarnMsg div").append(warning);
    }
    setTimeout("ClearWarning('"+ctlid+"_Warn')", 15000);
    return true;
    }// Validate - if (rule)
};
var focusSelect = function(ctlid, e) {
    ctl = $("#"+ctlid)[0];
    ctl.focus();if (ctl.type=="text"){ctl.select();}
};
var ClearWarning = function(id) {
    $("#"+id).remove();
    if ($("#WarnMsg div p").length === 0) {
        $("#WarnMsg").hide("slow").remove();
    }    
};

// Lets add a warning function
var required = function(e) {
    ClearCtlBg(this);
    Validate(this, true, "This file is required.");
    Validate(this, (this.value.length > 3), "This file is required.");
};
In my html, I can do something simple:
<html>
<head>
    <!-- Include CSS file -->
    <!-- Include jQuery file -->
    <!-- Include Javascript file -->
</head>
<body>
    <input type="text" id="txtName" class="name" />
    <script type="text/javascript">
    $(document).ready(function(){
        $(".name").blur(required);
        //$(".someddl").change(ddlvalidator);
    });
    </script>
</body>
</html>
To trigger the warning, click in the text box below and click outside the text box. A popup should slide into view on the upper right hand corner. This can be customized by editing the .animate({ top: String(Number(getScrollXY()[1])+3)+"px" }, 750 );.


For example:

It may seem weird to use jQuery to attach a warning to one control. The logic behind this architecture is for use on input tables or Grid Views where every control in a column will have the same class name and each needs to be evaluated.

No comments: