JavaScript onbeforeunload — як попереджати юзера про незбережені дані у формі

Напевно кожному знайома ситуація: пишеш ти комусь довжелезне повідомлення, допис на форумі, або просто заповнюєш велику форму реєстрації, і тут РАПТОВО випадково клацаєш на якесь посилання чи закриваєш вкладку… і все те, що ти натхненно писав протягом останніх n хвилин, зникає. Кілька секунд йде на усвідомлення того, що сталось, і прийняття факту, що вже нічого не повернути. Після цього ти проклинаєш розробників сайту, рвеш на собі волосся, розбиваєш монітор, хапаєшся за сокиру і йдеш вбивати.

Тому хороший сайт повинен попереджати юзера про те, що у формі залишились незбережені дані, коли той пробує покинути сторінку. Звісно, таке не варто робити для всіх форм підряд. Наприклад, для форми логіну, де користувач вводить лише юзернейм і пароль, це зайве. Але для форм, де передбачається введення великої кількості даних, видавати таке попередження є правилом хорошого тону.

Робиться таке дуже просто через івент onbeforeunload у JavaScript.

onbeforeunload спрацьовує тоді, коли користувач пробує покинути сторінку (будь-яким чином — закрити вікно/вкладку, оновити сторінку, перейти за якимось посиланням і тд.) Щоб при закритті сторінки відкрилось попередження, треба просто обробити цей івент. А саме, коли він спрацьовує, треба викликати якусь функцію, яка має всього-на-всього повернути певний текст. Браузер використає цей текст в якості повідомлення, яке буде показано користувачу. Щоправда, Firefox ігнорує текст, наданий сайтом, і завжди показує своє стандартне повідомлення. Але це ще нічого, наприклад, Opera взагалі не вміє працювати з onbeforeunload, там сторінка просто закриється, і все. В Chrome, IE, Safari все добре :)

Найпростіший приклад обробки івенту onbeforeunload:

window.onbeforeunload = function() {
  return "Точно закрити сторінку?";
}

Якщо додати до сторінки такий скрипт, то при спробі покинути її, у Chrome ми побачимо таке повідомлення:

А у Firefox — таке:

Тепер нам потрібно зробити так, щоб це повідомлення відображалось не завжди, а лише тоді, коли користувач вніс якісь зміни у форму на сторінці. Як визначити, чи користувач вніс зміни у форму? Просто порівняти ті дані, які були у формі при завантаженні сторінки, і ті, які є у формі при спрацюванні onbeforeunload.

Для початку створимо сторінку з простою формою, яка складається лише з одного поля для тексту і однієї кнопки. HTML код:

<html>
<head>
  <title>JavaScript onbeforeunload приклад</title>
</head>
<body>
  <form method="post" action="ok.html">
    <textarea name="text" id="someForm" style="width:550px; height:350px;"></textarea><br>
    <input class="button" name="msubmit" type="submit" value="Надіслати">
  </form>
</body>
</html>
А тепер напишемо скрипт, який перетворить цю туфтову і примітивну форму в круту та інтерактивну:
var oldData;
var newData;

function saveOldData() {
  oldData = document.getElementById("someForm").value;
}
window.onload = saveOldData;

function toCloseOrNotToClose() {
  newData = document.getElementById("someForm").value;
  if (newData != oldData) {
    return "Є незбережені дані. Точно закрити сторінку?";
  }
}
window.onbeforeunload = toCloseOrNotToClose;

Скрипт зовсім елементарний. При відкритті сторінки спрацьовує івент onload і викликається функція saveOldData(). Вона витягує вміст з форми someForm і записує його у змінну oldData. Далі на сторінці щось відбувається, юзер щось робить, можливо, міняє вміст форми, а може й ні. При закритті сторінки спрацьовує onbeforeunload, викликається функція toCloseOrNotToClose(). Спершу вона зберігає поточний вміст форми у змінну newData, а потім перевіряє, чи oldData і newData однакові. Якщо ні, значить користувач вніс зміни у форму, і тоді відображається попередження.

Все круто, але є одне але :). Якщо для надсилання даних із форми використовується метод POST, то коли юзер натисне кнопку «Надіслати», форма спробує перенаправити його на іншу сторінку, і йому, знову ж таки, буде відображено повідомлення про те, що у формі є незбережені зміни. У цьому випадку показувати таке повідомлення тупо, бо ж юзер якраз і хоче зберегти ці зміни, натискаючи кнопку. Тому треба трошки підправити наш код.

Додамо змінну isDataSubmitted, яка буде відповідати за те, чи були дані з форми збережені. За замовчуванням вона буде дорівнювати false. Тепер додамо функцію submitData(), яка буде міняти значення isDataSubmitted на true. В функцію toCloseOrNotToClose() крім перевірки на те, чи нові дані з форми відрізняються від старих, додамо перевірку на те, чи isDataSubmitted дорівнює true.

var oldData;
var newData;
var isDataSubmitted = false;

function submitData() {
  isDataSubmitted = true;
}

function saveOldData() {
  oldData = document.getElementById("someForm").value;
}
window.onload = saveOldData;

function toCloseOrNotToClose() {
  newData = document.getElementById("someForm").value;
  if (newData != oldData && isDataSubmitted == false) {
    return "Є незбережені дані. Точно закрити сторінку?";
  }
}
window.onbeforeunload = toCloseOrNotToClose;

І зробимо так, щоб при кліку на нашу кнопку викликалась функція submitData().

<html>
<head>
  <title>JavaScript onbeforeunload приклад</title>
</head>
<body>
  <form method="post" action="ok.html">
    <textarea name="text" id="someForm" style="width:550px; height:350px;"></textarea><br>
    <input class="button" name="msubmit" type="submit" value="Надіслати" onClick="submitData();">
  </form>
</body>
</html>

Ось і все. Тепер попередження буде відображатись тоді, коли юзер вніс зміни у форму і покидає сторінку таким чином, що ці зміни можуть втратитись.

Архів з цим прикладом тут.

На додаток, стаття від Студії Лебедєва про те, як робити хороші з точки зору юзабіліті форми http://www.artlebedev.ru/tools/technogrette/etc/forms/