Предупредить пользователя перед тем, как покинуть веб-страницу с несохраненными изменениями - PullRequest
290 голосов
/ 06 сентября 2011

У меня есть несколько страниц с формами в моем приложении.

Как я могу защитить форму таким образом, чтобы, если кто-то уходит или закрывает вкладку браузера, ему предлагалось подтвердить, что он действительно хочет оставить форму с несохраненными данными?

Ответы [ 15 ]

1 голос
/ 07 августа 2018

Краткий ответ:

let pageModified = true

window.addEventListener("beforeunload", 
  () => pageModified ? 'Close page without saving data?' : null
)
1 голос
/ 11 июня 2015

Подробное объяснение можно посмотреть здесь: http://techinvestigations.redexp.in/comparison-of-form-values-on-load-and-before-close/ сравнение значений формы при загрузке и до закрытия

Основной код:

function formCompare(defaultValues, valuesOnClose) {

    // Create arrays of property names
    var aPropsFormLoad = Object.keys(defaultValues);
    var aPropsFormClose = Object.keys(valuesOnClose);

    // If number of properties is different,
    // objects are not equivalent
    if (aPropsFormLoad.length != aPropsFormClose.length) {
        return false;
    }

    for (var i = 0; i < aPropsFormLoad.length; i++) {
        var propName = aPropsFormLoad[i];

        // If values of same property are not equal,
        // objects are not equivalent
        if (defaultValues[aPropsFormLoad]+"" !== valuesOnClose[aPropsFormLoad]+"") {
            return false;
        }
    }

    // If we made it this far, objects
    // are considered equivalent
    return true;

}

//add polyfill for older browsers, as explained on the link above

//use the block below on load
    for(i=0; i < document.forms[0].elements.length; i++){
    console.log("The field name is: " + document.forms[0].elements[i].name +
        " and it’s value is: " + document.forms[0].elements[i].value );
    aPropsFormLoad[i] = document.forms[0].elements[i].value;
    }

//create a similar array on window unload event.

//and call the utility function
    if (!formCompare(aPropsOnLoad, aPropsOnClose))
    {
    //perform action: 
    //ask user for confirmation or
    //display message about changes made
    }
1 голос
/ 17 марта 2015

Добавление к идее @codecaster Вы можете добавить это на каждую страницу с формой (в моем случае я использую ее глобально, так что только в формах будет это предупреждение) измените его функцию на

if ( formSubmitting || document.getElementsByTagName('form').length == 0) 

Также ставьте на формы отправки, включая ссылки входа и кнопки отмены, чтобы при нажатии кнопки «Отмена» или отправки формы предупреждение не появлялось и на каждой странице без формы ...

<a class="btn btn-danger btn-md" href="back/url" onclick="setFormSubmitting()">Cancel</a>
0 голосов
/ 17 июля 2019

Протестировано универсальное решение Эли Грея, работавшее только после того, как я упростил код до

  'use strict';
  (() => {
    const modified_inputs = new Set();
    const defaultValue = 'defaultValue';
    // store default values
    addEventListener('beforeinput', evt => {
      const target = evt.target;
      if (!(defaultValue in target.dataset)) {
        target.dataset[defaultValue] = ('' + (target.value || target.textContent)).trim();
      }
    });

    // detect input modifications
    addEventListener('input', evt => {
      const target = evt.target;
      let original = target.dataset[defaultValue];

      let current = ('' + (target.value || target.textContent)).trim();

      if (original !== current) {
        if (!modified_inputs.has(target)) {
          modified_inputs.add(target);
        }
      } else if (modified_inputs.has(target)) {
        modified_inputs.delete(target);
      }
    });

    addEventListener(
      'saved',
      function(e) {
        modified_inputs.clear()
      },
      false
    );

    addEventListener('beforeunload', evt => {
      if (modified_inputs.size) {
        const unsaved_changes_warning = 'Changes you made may not be saved.';
        evt.returnValue = unsaved_changes_warning;
        return unsaved_changes_warning;
      }
    });

  })();

. При модификации его значения исключается использование target[defaultValue] и используется только target.dataset[defaultValue] для хранения реального значения по умолчанию..

И я добавил «сохраненный» прослушиватель событий, где «сохраненное» событие будет вызвано вами самостоятельно при успешном сохранении.

Но это «универсальное» решение работает только в браузерах,не работает в веб-представлении приложения, например, wechat браузерах.

Для того, чтобы оно работало в wechat браузерах (частично) также, еще одно улучшение:

  'use strict';
  (() => {
    const modified_inputs = new Set();
    const defaultValue = 'defaultValue';
    // store default values
    addEventListener('beforeinput', evt => {
      const target = evt.target;
      if (!(defaultValue in target.dataset)) {
        target.dataset[defaultValue] = ('' + (target.value || target.textContent)).trim();
      }
    });

    // detect input modifications
    addEventListener('input', evt => {
      const target = evt.target;
      let original = target.dataset[defaultValue];

      let current = ('' + (target.value || target.textContent)).trim();

      if (original !== current) {
        if (!modified_inputs.has(target)) {
          modified_inputs.add(target);
        }
      } else if (modified_inputs.has(target)) {
        modified_inputs.delete(target);
      }

      if(modified_inputs.size){
        const event = new Event('needSave')
        window.dispatchEvent(event);
      }
    });

    addEventListener(
      'saved',
      function(e) {
        modified_inputs.clear()
      },
      false
    );

    addEventListener('beforeunload', evt => {
      if (modified_inputs.size) {
        const unsaved_changes_warning = 'Changes you made may not be saved.';
        evt.returnValue = unsaved_changes_warning;
        return unsaved_changes_warning;
      }
    });

    const ua = navigator.userAgent.toLowerCase();

    if(/MicroMessenger/i.test(ua)) {
      let pushed = false

      addEventListener('needSave', evt => {
        if(!pushed) {
          pushHistory();

          window.addEventListener("popstate", function(e) {
            if(modified_inputs.size) {
              var cfi = confirm('确定要离开当前页面嘛?' + JSON.stringify(e));
              if (cfi) {
                modified_inputs.clear()
                history.go(-1)
              }else{
                e.preventDefault();
                e.stopPropagation();
              }
            }
          }, false);
        }

        pushed = true
      });
    }

    function pushHistory() {
      var state = {
        title: document.title,
        url: "#flag"
      };
      window.history.pushState(state, document.title, "#flag");
    }
  })();
0 голосов
/ 06 сентября 2011

Прежде всего, большинство браузеров имеет эту функцию по умолчанию. А зачем тебе это вообще? Почему бы не сохранить форму синхронизированной? Я имею в виду, сохранить его на любое изменение, не дожидаясь отправки от пользователя. Как это делают контакты Google. Конечно, если только все поля в форме являются обязательными. Пользователям не нравится, когда их заставляют что-то заполнять без возможности уйти, чтобы подумать, нужно ли им это. :)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...