Progressive filtering using jQuery


Here is a simple plugin to filter a result set using jQuery

You "attach" the plugin to a text field and then as you type in that field only the matching records are shown

<html>
<head>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js"></script>
<script type="text/javascript" src="jprogressive.js"></script>
<script type="text/javascript" src="dumpObject.js"></script>
<style>
.jp{display:none;}
</style>
<script language="JavaScript">
<cfparam name="url.startsWith" default="true">
$(document).ready(function(){
    $(".jp").hide(); //hide all records first
//attach the plugin to text field myinput
    $("#myinput").jprogressive({attribute:"value",dataclass:"jp",startsWith:<cfoutput>#lcase(url.startsWith)#</cfoutput>});
    // has nothing to do with plugin we just want to see which words user has selected
$("#mySelect").bind("click",function(){
        foo=[];
        $(".jp >
:checked").each(function(){
            foo.push(this.value);    
        });
        alert(foo);
    });
})
</script>
<body>
<!-- Just getting some words to choose from -->
<cfquery name="q"datasource="cryptogram" maxrows="500">
Select word from tbl_dictionary
where id%10=0
</cfquery>
<input type="button" value="Select" id="mySelect">
<input type="text" id="myinput"> <!-- this is where we type our filter criteria
<!-- Notice that we give all the divs a common class-->
<cfoutput query="q">
<div class="jp" value="#q.word#">#q.word#<input type="checkbox" value="#q.word#"></div>
</cfoutput>

</body>
</html>

Here is the plugin

(function($){
    $.fn.extend({
        jprogressive:function(_options){
            var options={};
            var config={
                attribute:"value",
                dataclass:"jprogressive",
                startsWith:true
                };
            //entry point    
            options=$.extend(options,config);
            options=$.extend(options,_options);
            return this.each(function(){        
                $(this).bind("keyup",function(){
                    $("." + options.dataclass).hide(); //hide everything and then show matches
                    if ($(this).val() != "") {
                        if (options.startsWith) {
                            $("." + options.dataclass + "[" + options.attribute + "^=" + $(this).val() + "]").show();
                        }
                        else {
                            $("." + options.dataclass + "[" + options.attribute + "*=" + $(this).val() + "]").show();
                        }
                    }
                });        
            });
        }
    })
})(jQuery);

The query has three options:

  1. attribute: The attribute we look for in the displayed elements. The default is "value" It could be "name" or whatever else you choose
  2. dataclass: This is the class which characterises all your display elements. In our example we use "jp"
  3. startsWith: if true we look for elements whose attribute value "starts with" the filter, else the value "contains" the filter
  4. Feel free to copy and use the above snippets. There is no error checking etc but you may find it useful.

    Try it here

A jQuery plugin for Zooming.


I have worked on a jQuery plugin to allow a user to pan over a low res image and zoom in on the same position in a hi res image. The code is entirely client-side and depends on both low and high res images being available. There is no resampling of the image.The plugin may be applied to multiple images on a page. Before delving into the code let's take a look at an example. (The video seems to hang at a couple of points. Toggling the pause button will allow the video to continue.)

Let's look at the code in the rendering page.

<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js"></script>
<script type="text/javascript" src="jzoom.js"></script>
<script language="JavaScript">
<cfif url.styleit>
$(document).ready(function(){
    $("#view2").hide();

$("#second").jzoom("init",src:"coinhires.jpg",id:"view2",width:600,height:400,wrapperClass:"wrapperstyle",hideOnExit:false});
    $("#first").jzoom("init",{src:"hires.jpg",id:"view2",width:400,height:400,wrapperClass:"wrapperstyle2",hideOnExit:true});
})
<cfelse>
$(document).ready(function(){
    $("#view2").hide();
    $("#second").jzoom("init",{src:"coinhires.jpg",id:"view2",width:600,height:400,hideOnExit:false});
    $("#first").jzoom("init",{src:"hires.jpg",id:"view2",width:400,height:400,hideOnExit:true});
})
</cfif>
</script>

ColdFusion is not necessary for this to work. It is only used as a convenience here so I can show different behavior depending on the url parameter. The plugin works fine in a pure html/javascript page.

Other than loading jquery and the jzoom plugin you only have one line of javascript per image to be zoomed. The call to the init method on the plugin initializes the code and binds the mouse events. We will look at the options in a bit.

<img id="first" src="lores.jpg">
<img id="second" src="coinlores.jpg">
<div id="container">
<img id="view2" class="foobar" style="display:none" src="" >
</div>

The html code very simple. Two img tags for the lores images and an img tag (with an empty src) for the hires display. It is not necessary to surround the hires image with a div, but doing so (with the surrounding div having position:absolute) will prevent the page from reflowing when the hires image is toggled between shown and hidden.

    Options:
    Name              Default Value         Purpose
    src               ""                    The src of the hires image
    id                ""                    The id of the img tag of the hires image
    width             400                   The width of the viewport
    height            300                   The height of the viewport
    wrapperClass      ""                    The class of a generated div which will surround the hires image.
                                              Necessary if you want a border around the hires image.
    hideOnExit        true                  If true the hires image will be hidden when you mouse out of the lores image
 
			
      Methods:
    init           Initializes the plugin, sets options and binds mouse events
    remove         Removes the wrappers around the hires image, unbinds the mouse events, displays the entire hires image
 			 
      Usage:
 	$(selector).jzoom(method,options)
 	      where selector is the id of the lores image, options is an object containing the options.
 			 	
        Example 1:
 			 	
        $("#second").jzoom("init",{src:"coinhires.jpg",id:"view2",width:600,height:400,wrapperClass:"wrapperstyle",hideOnExit:false});

     The lores image has an id of "second", coinhires.jpg is the src of the hires image which has an id of "view2", 
     a 600x400 portion of the hires image will be displayed. This portion will have the style "wrapperstyle" applied to the
     surrounding div and the image will not be hidden when you mouse out of the lores image.

       Example 2:
 			 	
       $("#second").jzoom("remove");

       The entire hires image will display and mousing over the lores image will no longer have any effect. 
       If you want to enable zooming again you would have to initialize the plugin again.

The options and sample usage are shown above.

How does the plugin work?

The plugin wraps the target hires image tag in two divs. One to clip the image(overflow:hidden) and another to position the hires image so that the appropriate portion is shown. Thanks to the folks at ajaxBlender.com (developers of jclip). I have modified the process used in jclip to have the position based on the mouse position in the lores image.

Let's look at the firebug output which shows the html after the init function runs.

Suggestions welcome. The plugin is here.

Overriding OS alerts and confirms using jQuery

I have recently been tasked with unifying the look and feel of a large application. The application makes use of the jQuery dialog for notifications as ajax operations process but there are a large number of places where the application uses OS alerts and confirms.

You cannot just replace these calls with a dialog because displaying the dialog does not stop downstream processing like alerts and confirms do. In the alert case this is usually not important unless you have code like:

alert("Sending you to google");
location.href="http://www.google.com";

In the above case you will go toodling off to google before the replacement for the alert has been dismissed. Fortunately in our application this use case doesn't occur so replacing the alert with a dialog works fine.

Replacing the alert window

The following will replace the native alert with a jQuery dialog (jqwindow is a plugin wrapper for the dialog):
$(document).ready(function (){
	window.alert=function(mess){
		$("#modal").jqwindow("alert",mess);
	};
     }
);

Unfortunately the above approach does not work in IE. IE will not let you override the native alert function so you have to change all the alerts in your code line with a new function like jqAlert.

Replacing the confirm window

The confirm window is more complicated because we absolutely have to stop processing until the user responds to the dialog. Fortunately we can use queuing in jQuery by:

  1. pushing the dialog onto the queue
  2. pushing the function to be executed when the dialog closes onto the queue
  3. dequeuing the dialog when it closes
  4. dequeuing the next function after it executes.

Sample code

$(document).ready(function (){
   $(".confirm").click(function(){
     $("#modal3").jqwindow("confirm",{message:"After this dialog is confirmed an alert will fire",nextFunction:function(){
        $(this).dequeue();
	if($(this).data("returnValue")){   //the wrapper will set the user response in the data of the element
	  $("#modal3").jqwindow("alert","Confirmation ACK");
	}else{
	   $("#modal3").jqwindow("alert","NACK on confirmation");
	}
     }
    }
   );  //closes jqwindow call
  });  //closes onclick definition
}); //closes document.ready
      

Comments or suggestions appreciated.

Using .data in jQuery

I am working on a redesign of a cryptogram site. (http://www.dailycryptogram.com). The old version works but has a lot of cumbersome javascript written long before jQuery was available. The handlers for keypress are complex and refreshing the display involves looping over the arrays of the encrypted, unencrypted, used, and available letters. There has to be an easier way.

What have I chosen is to add attributes to the DOM elements where the letters are displayed so the event handlers have all the information necessary without looking it up:

 $(".enc_"+el).data("encLetter",el);
 $(".enc_"+el).data("solLetter",sl);
 $(".enc_"+el).data("dspLetter","");

The event handlers become much simpler:

   $(".chosen").live("click",function(){
     try{
	if($(this).data("dspLetter")!=""){    //we are replacing letter so show original as available
	   $(".choice."+$(this).data("dspLetter")).show();
	}
	if($.chosenLetter==""){ //if no letter is chosen make sure textbox is empty
	    $(".enc_"+$(this).data("encLetter")+">.letterbox").attr("value","");
	}
	else{    //fill the textbox and remove the letter from the available pool
	    $(".enc_"+$(this).data("encLetter")+">.letterbox").attr("value",$.chosenLetter);
	    $("."+$.chosenLetter).hide();
	}
        $(".enc_"+$(this).data("encLetter")).data("dspLetter",$.chosenLetter);//update data on all occurences of the letter			
	$.isSolved();
	$.chosenLetter="";
	$.destinationLetter="";
    }catch(except){alert(except)}
});

Also checking for a correct solution is quite easy:

$.isSolved=function(){
   var solved=true;
   $(".bb>.chosen").each(function(i){ // elements of the bb class holds the data infomation
	solved=solved && ($(this).data("solLetter")==$(this).data("dspLetter"));
   });
   if(solved){
	$(".letterbox").addClass("greenboxes").removeClass("letterbox");
   }
}

Demo

The demo source gives more detais. To solve the cryptogram you can either drag an available letter(red) into the textbox or just type in the textbox. To remove a letter just click on it. You will not be able to enter a letter twice.

Attaching data to a DOM element is quite useful and simple to do. The old way of attaching a primary key to the id of a table row no longer is necessary. Attaching the pk to the data of the row is just as easy and avoids the worry of making sure the id is unique on the page.

Hiding Controls while printing

I have a page Print page where users can print cryptograms.

There are a lot of controls on the page which I really don't want to show up when the page is printed but are necessary to format the page prior to printing.

There is a simple solution using jQuery:

I wrap the controls in divs with id's of bottombuttons and pb and then add a handler for clicking the print button.

Here is the handler for the Print Button:

$("#button5").click(function() {
        toggleButtons("No");
        window.print();
        toggleButtons("Yes");
        return false;
    })
and here is the function toggleButtons:
function toggleButtons(on){
    if(on=="Yes"){
        $("#botombuttons").show();
        $("#pb").show();
    }
    else{
            $("#botombuttons").hide();
            $("#pb").hide();
    }
    return true;
}

The result is that the controls disappear, then the window is printed and the controls restored.

BlogCFC was created by Raymond Camden. This blog is running version 5.9.4.001. Contact Blog Owner