viernes, 29 de junio de 2012

Disable HTML elements programatically

They asked me to disable / disable some html elements in the client side with javascript so the user cannot interact with cerrtain parts of an html document at some point. My sollution, based on jquery, is to put an html element in front of each elments we want to disable. This mask should be transparent and positioned absolutely with a big z-index. The following is an HTML document that demonstrates the problem and contains a simple implementation of a sollution. Hope it can be usefull to somebody needing something similar.


< !doctype html>
< html lang="es-UY">

< head>
< script src="http://ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js" type="text/javascript">< /script>
< title>test< /title>
< /head>

< script type="text/javascript">
var lib = {
idCounter: 0, 
disablers: [], 
/**
 * disable the given html elements by putting a mask html element with the same bounds in front of each given element. use disablers.enable for enabling them programatically. 
 * @param el - a reference to the elements to disable. For example, ".c1" will disable all elements of CSS class c1, "#el1" will disable the element id el1.
 * @param maskCSS - optional - an object with extra CSS value for the mask, for example: {"background-color": "red", opacity: 0.2}
 * @return the masks elements (array of jquery object) created
 */
disable: function(el, maskCSS) {
 var masks = []; 
 $(el).each(function(){
  masks.push(lib.disableOne(el, maskCSS)); 
 });
 return masks; 
}, 
/**
 * the same as disable, but treat the given object as a single element 
 */
disableOne: function(el, maskCSS) {
 var id = $(el).attr("id"); 
 if(!id) {
  lib.idCounter++;
  id="disabler_"+lib.idCounter; 
  $(el).attr({"id": id}); 
 }
 if(!lib.disablers[id]) {
  var dis = document.createElement("div"); 
  document.body.appendChild(dis); 
  lib.disablers[id]=$(dis); 
 }
 var dis = lib.disablers[id];
 $(dis).css({
  "position": "absolute", "z-index": 99999, "display": 
  "block", "left": $(el).offset().left, "top": $(el).offset().top, 
  "width": $(el).width(), "height": $(el).height(), "opacity": 0.4, "background-color": "black"
 });  
 $(dis).show(); 
 if(maskCSS)
  $(dis).css(maskCSS); 
 return dis; 
},
enable: function(el) {
 if($(el).attr("id") & & lib.disablers[$(el).attr("id")]) {
  var dis = lib.disablers[$(el).attr("id")]; 
  dis.hide(); 
 }
}
}; 
window.disabler=lib; 
< /script>

< p>The following table contains a disabled element< /p>

< table>
< tr>< td id="el1">< button>hello< /button>< /td>< td>< p>alskjdlaksjd< /p>< /td>< /tr>
< tr>< td>< button onclick="window.disabler.enable('#el1')">click me for enable < /button>< /td>< td>< p>als< a href="">kjdlaksj< /a>d< /p>< /td>< /tr>
< /table>

< script type="text/javascript">
//examples: 

//disable a single element with id "el1"
disabler.disable("#el1"); 

//disable all < a> elements: 
disabler.disable("a"); 
< /script>


< /body>


< /html>

domingo, 10 de junio de 2012

JavaScript function inBetween

JavaScript function inBetween - be notified when a javascript function is called N times in between a time lapsus of T milliseconds.

This idea borned when reading the work of Ben Alman for function throttle : http://benalman.com/projects/jquery-throttle-debounce-plugin/

In that case the problem is to execute a function no more than once every T milliseconds. This is very helpful when you want to control how much an event handler is called back. I put a common example for events that are called intensively, like mousemove, or drag in my raphaël tutorial: http://cancerbero.vacau.com/raphaeltut/index.html#sec-events-function-throttle   There I explain very usefull cases of function throttle.

The thing is that function throttle made me think on the opposite problem: being notified when a function is called N times in a time lapsus of T milliseconds. This can be useful for registering double, triple, cuadruple clicks, and in general nth-clicks events.I call this, function inBetween (don't have a better name, suggestions accepted). See for example, how I would use inBetween with jquery for neing notified when a triple click occurs in between 1000 ms:

function click3Hendler(evt) {
    alert(this.clickCount+" clicks detected. last click xcoord: "+evt.clientX);
};
$("#someElement").click(inBetween(3, 1000, cb, {clickCount: 3}));

This is a complete working example: http://jsfiddle.net/RP7mW/11/.

Also there is a section in my raphaël tutorial for registering nth-click listeners to raphaël shapes using inBetween(). Link: http://cancerbero.vacau.com/raphaeltut/index.html#sec-events-inbetween

And this is the inBetween function code:
        
/**
 * inBetween resolves the problem of being notified when a function is called N times in in between a time lapsus of T ms.
 * @param n the amount of times.
 * @param t the time lapsus in ms
 * @param callback - the function to be called when the returned fcuntion is called at least n times in a lapsus of n ms.
 * @return a new function - when that function is called n times in a lapsus
 of t ms, then callback function will be called using the context object as
 the callback context.
 */
function inBetween(n, t, callback, context) {    
    var sb = [];
    sb.push("var that = arguments.callee; ")
    sb.push("var thisTime = new Date().getTime(); ")
    sb.push("var arr = that['ARR'];");
    sb.push("if(!arr){");
    sb.push("    arr = []; ");
    sb.push("    for(var i = 0; i < that['N']; i++) arr.push(thisTime); ");
    sb.push("    that['ARR'] = arr;");
    sb.push("    that['COUNT']=0");
    sb.push("}");
    
    sb.push("that['COUNT']++; ");;
    sb.push("arr.push(thisTime);");
    sb.push("var lastTime = arr.shift();");
        
    sb.push("if(that['COUNT'] >= that['N']) {");
    sb.push("    that['COUNT']=1; ");
    sb.push("    for(var i = 0; i < that['N']; i++) arr[i] = thisTime; ");
    sb.push("    if(thisTime-lastTime < that['T']) ");          
    sb.push("        that['CB'].apply(that['CTX'], arguments); ");
    sb.push("}");
        
    var fn = new Function(sb.join(""));    
    fn['N']=n;
    fn['T']=t;
    fn['CB']=callback;
    fn['CTX']=context;
    return fn;        
}; 

Hope this can be of some use for those who need some advance event - function stuff like this.