Question

I would like to over ride the function associated with an elements onclick event when the screen is in "edit" mode. Then when done editing put the original function back.

So, originally I have the following:

<script type="text/javascript">
    var showAddNewForum = function(){
        dojo.byId("NewForum").className = dojo.byId("NewForum").className == 'hidden'?'':'hidden';
    }
</script>
<a  onclick="showAddNewForum()" class="editable" id="AddNewForum" name="AddNewForum" style="cursor:pointer;">New Forum</a>
        <div class="hidden" id="NewForum" name="NewForum">
            <form action="">
                <table>
                    <tr><td><label>Title</label></td><td><input type="text" id="newForumTitle"/></td></tr>
                    <tr><td><label>Description</label></td><td><input type="text" id="newForumDescription"/></td></tr>
                    <tr><td><label>Moderators</label></td><td><input type="text" id="newForumnModerators"/></td></tr>
                </table>
                <input type="button" value="submit"/>
            </form>
        </div>

At the top of the page there is an Admin button that will put the page in configuration mode. All elements with a class of editable will be in configuration mode so the element can be configure through a little form. The idea is certain controls on the screen will have different behavior based on a users role. example: If administrator show it other wise do not display the control.

This is done with the following:

    activateAdministration = function() {
        if (editOnClickHandle.length>0) {
            dojo.forEach(editOnClickHandle,function(item){dojo.disconnect(item)});
            editOnClickHandle = [];
        }else {
            dojo.query(".editable").forEach(function(node,idx,arr){
                var handler = dojo.connect(node,"onclick",function(e){
                    console.debug(node.onclick);
                    var adminWindow = document.getElementById("adminWindow");
                    adminWindow.className = "displayInline";
                    adminWindow.style.top = (e.layerY + 0) + "px";
                    adminWindow.style.left = (e.layerX + 0) + "px";
                    document.getElementById("adminElementId").value = node.id;
                    document.getElementById("adminCurrentUrl").value = location.href;
                    document.getElementById("adminClass").value = node.className;
                });
                editOnClickHandle.push(handler);
            });
        }
    }
<a class="tool" style="cursor:pointer;" onclick="activateAdministration();">Admin</a>

So the problem is the original onclick function is still attached to the event. If it were a submit function or something like that then it would also fire which is not desired.

Can I set a handler to the original function, dissconnect it, add the new function, and then when done editing remove the new function and add the original one back?

Thanks for any tips, I hope the question is clear (probably not) happy to add more info if needed.

Was it helpful?

Solution

The way I would implement this is to always override the onclick handler for every editable object on the page with a handler that either simply delegated the call to the original or called the admin version.

You can wire this up pretty easy in dojo, like this:

  admin_mode = false;
  activateAdministration = function() {
    admin_mode = true;
  };

  editClick = function(e) {
    console.debug("..in admin edit function; this=%o; e=%o", this, e);
    // do your admin stuff here
    // var node = this;
  };

  dojo.query('.editable').forEach(function(node) {

    if (node.onclick) {
      var original_onclick = node.onclick;
      node.onclick = function() {
        if (admin_mode) {
          console.debug("calling admin onclick handler");
          editClick.apply(node, arguments);
        } else {
          // delegate
          console.debug("calling original onclick handler");
          original_onclick.apply(node, arguments);
        }
      }
    }

  });

I tested it with HTML like this:

<a onclick="console.debug('I am the original!; this=%o; args=%o', this, arguments);" 
   class="editable"> 
  EDITABLE 
</a>

<a onclick="console.debug('Me too!; this=%o; args=%o', this, arguments);" 
   class="editable"> 
  ALSO EDITABLE 
</a>

OTHER TIPS

Do you control the code for the showAddNewForum() function and its insertion into the HTML? In that case, I would probably just use one handler, something like:

dojo.connect(dojo.byId("AddNewForum"), "onclick", function(evt) {
    if (dojo.hasClass(evt.target, "editable") {
        //do the editing work here
        doConfiguration(evt.target);
    } else {
        //do regular work here.
        doRegularAction(evt.target);
    }
});

Either that or hold on to the connect handles and unregister and register the right ones when you change the page to configuration mode.

First of let me say I HATE IE, especially IE6. It will be a good day indeed when my company finally moves off of IE6. So how I got around it is I set an attribute called oldclick to node.onclick and then set the node.onclick="" when I activateAdministration. On the flip side I disconnect the hadlers and add the oldclick back to the onclick.

var editOnClickHandle = []; 
activateAdministration = function() {
        if (editOnClickHandle.length>0) {//turn off administration
            dojo.forEach(editOnClickHandle,function(item){dojo.disconnect(item)});
            dojo.query(".editable").forEach(function(node,idx,arr){
                if(dojo.isIE){
                    node.onclick=node.getAttribute("oldclick");
                }else{
                    node.onclick=dojo.hitch(node.onclick,node.getAttribute("oldclick"));
                }               
            });
            editOnClickHandle = [];
        }else {//turn on administration
            dojo.query(".editable").forEach(function(node,idx,arr){
                var onClickName = new String(node.getAttribute("onclick"));
                var oldClickName = onClickName.substring(0,onClickName.indexOf("("));
                if(dojo.isIE){
                    node.setAttribute("oldclick",node.onclick);
                }else{
                    node.setAttribute("oldclick",oldClickName);
                }
                node.onclick="";
                editOnClickHandle.push(dojo.connect(node,"onclick",editClick));
            });
        }
    }

Not pertinent to this issue but for clarity I refactored the little admin window code out into a seprate function called editClick. It looks like:

editClick = function(e){
    console.debug(e);
    var adminWindow = document.getElementById("adminWindow");
    adminWindow.className = "displayInline";
    adminWindow.style.top = (e.layerY + 0) + "px";
    adminWindow.style.left = (e.layerX + 0) + "px";
    document.getElementById("adminElementId").value = e.target.id;
    document.getElementById("adminCurrentUrl").value = location.href;
    document.getElementById("adminClass").value = e.target.className;
}

So hopefully this will help someone in the future, thanks for your help jrburke. If anyone wants to pick apart my solution I am game for constructive input.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top