Thursday, June 2, 2011

All Your Links Are Belong To Me

I know this is ridiculously simple, but sometimes it's the simple things that just put a smile on a developer's face.

For years clients have had the same requirement for their web applications: "If a user makes changes on a page and navigates away, it should prompt them to save their changes." And for years there have been a number of different responses to this requirement. I've seen everything from "can't be done, the web doesn't work that way" to "we'll just set every control to AutoPostBack=true and store it in Session State" and everything in between. (That second example hurts just thinking about it.)

Being faced with that requirement yet again, a very simple approach just dawned on me. I'm sure I'm not the first person to do it this way, and I'm equally sure that there are better approaches. But this just feels cool...

// Track if the page has unsaved changes.
var unsavedChanges = false;

$(document).ready(function() {
  // Hijack all anchor tags and move their links to a custom attribute.
  $('a').each(function() {
    // Because i is one more than h, of course
    $(this).attr('iref', $(this).attr('href'));
    $(this).attr('href', '#');
    $(this).click(function() {
      if (unsavedChanges) {
        var conf = confirm('You have unsaved changes. Are you sure?');
        if (conf) {
          window.location.href = $(this).attr('iref');
        }
      } else {
        window.location.href = $(this).attr('iref');
      }
    });
  });

  // If anything changes, mark it as an unsaved change.
  $('input, textarea').change(function() {
    unsavedChanges = true;
  });

  // If the user saves a change, they no longer have unsaved changes.
  // (This isn't really necessary here because the save buttons on
  //  this page trigger post-backs, so the value will be reset anyway.
  //  But keep this here in case it's needed on other implementations.)
  $('input[id*=btnSave]').click(function() {
    unsavedChanges = false;
  });
});

(I guess it doesn't handle the back button, but then what does?)

2 comments:

  1. There is also the window "onbeforeunload" event that you can use to prompt before leaving the page for any reason.

    window.onbeforeunload = function() {
    return "Some text to prompt...";
    };

    Unfortunately it seems that not all browsers will use the return value for the confirm dialog. Some will use a generic message. There may be browser-specific ways to accomplish this that I'm not aware of.

    ReplyDelete
  2. I've run into issues where document-level and window-level events couldn't be relied upon for preventing a user from navigating away. It seems to me that people have used them in the past to try to keep users on a given page and the backlash was that browsers started performing the navigation regardless of the result of the event.

    It would definitely be a cleaner approach to just use a single higher-level event for this check. I wonder if "breaking the back button" was the exact driver for browsers to not allow it.

    ReplyDelete