Evitar el doble clic amb HTML
No sé qui es va inventar el doble clic però realment per usabilitat és una molt mala idea. Primer perquè a la gent gran no els gens fàcil això de clicar dos vegades seguides ràpidament al mateix punt. Però per altra banda el gran problema és que la gent que aprèn a fer doble clic, fa doble clic a tot arreu i sobretot allà on no toca.
Per exemple en una pàgina web és un molt mal lloc per fer doble clic. Si no està ben programat pot fer que la mateixa ordre s’executi dues vegades. O sigui que un doble clic en una transferència de diners, pot provocar que es facin dues transferències de diners iguals. L’altra gran problema del doble clic en una pàgina web és que si hi ha 100 usuaris operant normal, vol dir que el servidor haurà de servir com a molt 100 operacions alhora. Però si aquests usuaris es dediquen a fer doble clic. El nombre d’operacions que ha de poder aguantar el servidor es duplica.
Per complicar més les coses, el doble clic no es inhabilitar fàcilment ni des de l’HTML5, ni des del Javascript ni des del CSS3, per tant s’ha de trobar una solució programada que funcioni amb tots els navegadors, també amb els mòbils i que permeti navegar bé en un entorn web, o sigui que si es clica el botó enrere i endavant la pàgina ha de poder continuar funcionant bé.
Per solucionar aquest problema hi ha diverses solucions i estratègies:
- Amb jQuery utilitzant $(‘button’).one(“click”, …); en comptes de $(‘button’).bind(“click”, …); això evita que un mateix botó es pugui fer servir dues vegades. Ara bé si es clica el botó per saltar a una altra pàgina i un cop en aquesta nova pàgina es fa botó enrere, el botó continuarà inhabilitat i per tant la pàgina quedarà inoperativa.
- El mateix que abans es pot aconseguir amb javascript fent <button onclick=”this.disabled=true”> però amb els mateixos problemes alhora de fer botó enrere i a més a més no serveix per a botons <a href=”…”> </a>.
- Després tenim l’estratègia d’intentar evitar només els casos en què un doble click en un formulari enviï dues vegades el mateix formulari. Per exemple fent:
<button type="submit"onclick="this.disabled=true;this.form.submit();">
- O evitant el form.submit des del form.onsubmit. Per exemple fent
<form ... onsubmit="myButton.disabled = true; return true;">
- O evitant el form.submit des de la validació del formulari. Per exemple <script>
var submitting = false;</script><form ... onsubmit=" if (submitting) {return false;} else {submitting=true; return true;}">
- Una solució que va molt bé també és després de fer submit posar un div que ocupi tota la pantalla per sobre de tots els controls. El problema de fer això és que per IE cal buscar tots els <select> i posar-los a visible=false, sinó l’efecte no acaba de funcionar.
- També es pot evitar això des de la part del servidor. La forma més senzilla, és si utilitzeu Servlets poseu a la declaració de la classe Servlet implements SingleThreadModel . Això farà que totes les peticions del servidor es serveixin d’en una en una, siguin de quin usuari siguin. No és una solució molt recomanable, ja que és molt ineficient. Podeu veure’n un exemple del SingleThreadModel aquí.
- Des de la part del servidor alguns frameworks tenen algun filtre de seguretat per fer-ho millor. Si no, caldrà que programeu un semàfor al servidor i guardar-lo a l’httpsession.
- Tornant a la banda del client. En HTML5 existeix l’event ondoubleclick, així com existeix el onclick. Però francament per aquest camí ara mateix no es pot aconseguir gaire res.
- Una estratègia diferent és fer que cada botó s’inhabiliti només durant 5 segons. Per fer-ho he trobat algun exemple per Internet. Però jo he fet una proposta una mica diferent i més genèrica utilitzant jQuery.
$(document).ready(function()
{
$(".btn").click(function()
{
var lMilisNow= (new Date()).getTime();
this.setAttribute("data-one-click", lMilisNow);
setTimeout("oneClickTimeout()",5100);
this.disabled=true;
// Per evitar click en tag A. pointer-events funciona en FX, Webkit, IE11+
$(this).css("pointer-events","none");
});
}
function oneClickTimeout()
{
$(".btn[data-one-click]").each(function()
var lMilisNow= (new Date()).getTime();
var lMilisAtt= this.getAttribute("data-one-click");
lMilisAtt= lMilisAtt==null? 0: lMilisAtt;
if (lMilisNow-lMilisAtt >= 5000 )
{
this.disabled=false;
$(this).css("pointer-events","");
this.removeAttribute("data-one-click");
}
});
}