A few days ago I was reading a web design article that talked about the “Poka-Yoke” principle. It was such a funny phrase that I had to go to Wikipedia to figure out the definition. In a nutshell, the principle refers to the process of “idiot-proofing” human interaction with a process. This term is based on the Japanese words “yokeru” avoid and “poka” mistake, this is rather funny for me since I work for a Japanese company that has clearly never heard of this principle!

This term came to mind today while helping another programmer develop a system to prevent accidental in their UI. After discussing what needed to be done, it was clear that we needed to implement some client side logic to “prevent… guide…” the user into making the correct choices.

The UI was pretty straight forward, there will be a few buttons to do various actions on rows in a table. The users will select one or more rows for the table and then click the button of the action they want to perform. Sounds simple, right… We’ll it is and it’s also really easy to select the wrong row and/or the wrong button. Training is a big help in any process, but one you leave the users alone the system will be the only thing enforcing the rules.

Here is an example of a table where rows will be selected for deletion. I used a “asp:Table” to simulate the markup for a table that will be generated with a “asp:GridView”. In my past 5 years of working with ASP.NET, this was the first time I ever used or seen this control used.

    <div class="mediumTable">
        <asp:Button ID="DeleteButton" runat="server" Text="Delete" />
        <asp:Table ID="Table1" runat="server">
            <asp:TableHeaderRow>
                <asp:TableHeaderCell>Chk</asp:TableHeaderCell>
                <asp:TableHeaderCell>Product</asp:TableHeaderCell>
                <asp:TableHeaderCell>Stock</asp:TableHeaderCell>
            </asp:TableHeaderRow>
            <asp:TableRow>
                <asp:TableCell>
                    <asp:CheckBox ID="CheckBox1" runat="server" /></asp:TableCell>
                <asp:TableCell>Apples</asp:TableCell>
                <asp:TableCell>0</asp:TableCell>
            </asp:TableRow>
            <asp:TableRow>
                <asp:TableCell>
                    <asp:CheckBox ID="CheckBox2" runat="server" /></asp:TableCell>
                <asp:TableCell>Oranges</asp:TableCell>
                <asp:TableCell>100</asp:TableCell>
            </asp:TableRow>
            <asp:TableRow>
                <asp:TableCell>
                    <asp:CheckBox ID="CheckBox3" runat="server" /></asp:TableCell>
                <asp:TableCell>Pears</asp:TableCell>
                <asp:TableCell>150</asp:TableCell>
            </asp:TableRow>
            <asp:TableRow>
                <asp:TableCell>
                    <asp:CheckBox ID="CheckBox4" runat="server" /></asp:TableCell>
                <asp:TableCell>Watermelon</asp:TableCell>
                <asp:TableCell>2</asp:TableCell>
            </asp:TableRow>
        </asp:Table>
    </div>
    </form>

Here is the delete “prevention” system we came up. We had a few other ideas like, but after doing a few demos of each solution we decided on the following code that will be discussed below.

        $(document).ready(function() {
            $('#<%= DeleteButton.ClientID %>').click(function() {
                // Find all selected items 
                var CheckedCheckBoxes = $('#<%= Table1.ClientID %>').find("input[type='checkbox']:checked");
                // If there are no selected items, exit 
                if (CheckedCheckBoxes.length == 0) {
                    alert("You must make a selection before you can delete an item.");
                    return false;
                }
                // If there are selected items, loop 
                CheckedCheckBoxes.each(function() {
                    var isProcessed = $(this).parent().parent().find('td:last').text();
                    // If current item is already processed, set flat to true 
                    if (jQuery.trim(isProcessed) > 0) {
                        deletedProcessed = true;
                    }
                });
                // Check flag, if true stop ALL deletes and notify user 
                if (deletedProcessed) {
                    alert("You can't delete a product with stock!");
                    return false;
                }
                else {
                    // Do a final confirmation the user wants to delete the selection
                    var answer = confirm('Are you sure you want to delete your selection?');
                    return answer
                }
            });
        }); 

Since selection box was used for a few different functions (process orders, hold orders and edit orders) we needed a way to add a little extra checking to only the delete button. Our goals with the logic above was the following.

  1. Disables “Delete” if nothing has been selected.
  2. Disables “Delete” if the user selected a item should not be deleted (e.g. Has Inventory).
  3. Prompts the user to confirm the really want to delete the item.

In addition to these ideas, we also had the following ideas…

  1. Alert row # of items that don’t meet the business rule
  2. Highlight the row in “red” that don’t meet the business rule
  3. Physically “Disable” the delete button if they select an item that don’t meet the business rules

Mistakes are human nature and by double and triple checking what they are doing we can save the users and ourselves from many emails/phone calls/visits about how the system “deleted the data all by itself”.

Mistake proofing is such an easy idea to grasp and agree with, that it’s surprising that we don’t see more of it in the systems we using everyday. I’m sure I’ve developed a few bad ones, hopefully the audience using it was small and it didn’t mitigate itself into other things. Working for a Japanese company who has developed some of the worst internal tools I’ve ever seen (0 UI design effort), I’m sure to bring up the new Japanese “Poka-Yoke” principle that next time I talk to one of the developers in Japan.