I'm currently working on a project to write 100s of unqiue, internal web apps to live inside a common framework. Each app has completely different features and functions, but all share consistent UI patterns where buttons trigger actions that are specific to that app.

So, I had to solve the problem of "how can we simplify the development of each app's functions, without writing dozens of similar onClick event handler for every element type?"

Let's run through a scenario

A particular app may list the personal records of the staff in the company, and needs to have Edit and Delete functions to manipulate each record.

Each function needs to know the ID of the record its being acted on, so it can update the record accordingly, and, some records have restricted editing privileges.

As these functions perform different actions than every other app, the methods need to be custom to this app.

For the UI, we have a <table> of results, with Edit and Delete <button>s in each record's row.

The application's JavaScript file, will follow a pattern similar to this:

  var staff = {
  edit: function(id, restricted) {
    if (restricted === 'true') {
      alert('Loading *restricted* edit dialog for record #' + id);
    }
    else {
      alert('Loading edit dialog for record #' + id);
    }
  },

  delete: function(id) {
    alert('Deleted record #' + id);
  }
};

Now, how can we make our generic onClick event handler for <button>s know about this app's staff edit and delete functions?

Luckily, JavaScript allows you to access and call methods using array notation, and even allows you pass arguments as an array when doing so!

The setup

To achieve this, we'll need a click event on <button>s, a method to call the <button>'s unique action, and a way to tell the action which record to act upon, and whether they have restricted editing access.

JavaScript

Generic click event

I'm going to use jQuery here, simply because the syntax is a easier and more people are used to it.

  $('button').on('click', function(e) {
  e.preventDefault();
  callExternalMethod(this);
});

The magic generic method to call

But I'll use vanilla JS here for that added bit of performance.

  var callExternalMethod = function(elem) {
  var method, args;

  // [data-method] is a string of the method to call. Could be a single function (myFunc), or a property of an object (app.myFunc)
  if (elem.getAttribute('data-method')) {
    method = elem.getAttribute('data-method').split('.');
  }

  // [data-method-arguments] is a array-like string of the arguments to pass to the method separated with a ,
  if (elem.getAttribute('data-method-arguments')) {
    args = elem.getAttribute('data-method-arguments').split(',');
  }

  if (method && args) {
    // Strip any leading & trailing argument whitespace, just in case
    for (var i = 0; i < args.length; i++) {
      args[i] = args[i].replace(/(^\s+|\s+$)/g, '');
    }

    // If it's a single function to call 
    if (method.length === 1) {
      // Access and call the appropriate method and pass in the arguments as an array
      window[method[0]].apply(this, args);
    }
    else {
      // Otherwise do the same for methods as object properties
      window[method[0]][method[1]].apply(this, args);
    }
  }
};

HTML

A simple table-row pattern, with edit and delete <button>s that pass in the relevant methods to call and their arguments.

  <tr>
  <td>Joe Bloggs</td>
  <td>
    <button type="button" data-method="staff.edit" data-method-arguments="1, false">Edit</button>
    <button type="button" data-method="staff.delete" data-method-arguments="1">Delete</button>
  </td>
</tr>
<tr>
  <td>Joanna Blaggs</td>
  <td>
    <button type="button" data-method="staff.edit" data-method-arguments="2, true">Edit</button>
    <button type="button" data-method="staff.delete" data-method-arguments="2">Delete</button>
  </td>
</tr>

See it in action

I hope you can now see how you could use this technique to have any unique actions on a page, without ever defining multiple onClick event handlers.


6,396 1 16