sourcecode

변경 내용이 저장되지 않은 상태로 웹 페이지를 떠나기 전에 사용자에게 경고

copyscript 2022. 9. 13. 22:09
반응형

변경 내용이 저장되지 않은 상태로 웹 페이지를 떠나기 전에 사용자에게 경고

지원서에 양식이 있는 페이지가 몇 장 있어요.

누군가가 다른 곳으로 이동하거나 브라우저 탭을 닫았을 때 저장되지 않은 데이터로 폼을 남길 것인지 확인하는 메시지를 표시하도록 폼을 보호하려면 어떻게 해야 합니까?

짧은 오답:

이벤트를 처리하고 null이 아닌 문자열을 반환함으로써 이를 수행할 수 있습니다.

window.addEventListener("beforeunload", function (e) {
    var confirmationMessage = 'It looks like you have been editing something. '
                            + 'If you leave before saving, your changes will be lost.';

    (e || window.event).returnValue = confirmationMessage; //Gecko + IE
    return confirmationMessage; //Gecko + Webkit, Safari, Chrome etc.
});

이 접근법의 문제는 폼을 제출하면 언로드 이벤트도 발생한다는 것입니다.이 문제는 양식을 제출할 플래그를 추가하면 쉽게 수정됩니다.

var formSubmitting = false;
var setFormSubmitting = function() { formSubmitting = true; };

window.onload = function() {
    window.addEventListener("beforeunload", function (e) {
        if (formSubmitting) {
            return undefined;
        }

        var confirmationMessage = 'It looks like you have been editing something. '
                                + 'If you leave before saving, your changes will be lost.';
        
        (e || window.event).returnValue = confirmationMessage; //Gecko + IE
        return confirmationMessage; //Gecko + Webkit, Safari, Chrome etc.
    });
};

송신시에 세터를 호출합니다.

<form method="post" onsubmit="setFormSubmitting()">     
    <input type="submit" />
</form>

하지만 계속 읽어봐...

길고 정확한 답변:

사용자가 양식에서 아무것도 변경하지 않은 경우에도 이 메시지를 표시하지 않습니다.한 가지 해결 방법은beforeunload이벤트와 "syslog" 플래그를 조합하면 해당 플래그가 실제로 프롬프트가 트리거됩니다.

var isDirty = function() { return false; }

window.onload = function() {
    window.addEventListener("beforeunload", function (e) {
        if (formSubmitting || !isDirty()) {
            return undefined;
        }
        
        var confirmationMessage = 'It looks like you have been editing something. '
                                + 'If you leave before saving, your changes will be lost.';

        (e || window.event).returnValue = confirmationMessage; //Gecko + IE
        return confirmationMessage; //Gecko + Webkit, Safari, Chrome etc.
    });
};
    

" " " 를 구현합니다.isDirty방법은 다양합니다.

jQuery를 사용하여 폼 일련화를 수행할 수 있지만 이 방법에는 몇 가지 결함이 있습니다.먼저 어떤 형태로든 작동하려면 코드를 변경해야 합니다.$("form").each()'s) 단, 가장 큰jQuery입니다.serialize()는 이름 있는 비사용 요소에서만 동작하므로 비활성화 또는 이름 없는 요소를 변경해도 더티 플래그가 트리거되지 않습니다.이를 위한 해결 방법이 있습니다. 예를 들어 컨트롤을 활성화, 직렬화 및 다시 비활성화하는 대신 읽기 전용으로 만드는 방법이 있습니다.

그래서 일이 잘 풀리는 것 같다. 누르기를 들을 수 있습니다.이 이벤트에는 몇 가지 문제가 있습니다.

  • 마우스 입력을 통해 변경되는 확인란, 라디오 버튼 또는 기타 요소에 트리거되지 않습니다.
  • 키와 같은 관련 없는 키 누르기에 트리거됩니다.
  • JavaScript 코드를 통해 설정된 값에 트리거되지 않습니다.
  • 컨텍스트 메뉴를 통해 텍스트를 잘라내거나 붙여넣을 때 트리거되지 않습니다.
  • JavaScript를 통해 숨겨진 입력에 값을 저장하는 날짜 표시기, 체크박스/라디오 버튼 미화자 등의 가상 입력에는 사용할 수 없습니다.

이벤트JavaScript 코드에서 설정된 값에서도 트리거되지 않으므로 가상 입력에서도 작동하지 않습니다.

페이지모든 ( )에 이벤트를 바인드하는 오래된 브라우저에서는 동작하지 않으며 위의 모든 이벤트 처리 솔루션과 마찬가지로 실행 취소를 지원하지 않습니다.사용자가 텍스트 상자를 변경한 후 실행 취소하거나 확인란의 선택을 취소해도 양식은 여전히 더티로 간주됩니다.

그리고 특정 요소를 무시하는 등의 동작을 더 많이 구현하려면 더 많은 작업을 수행해야 합니다.

시간을 낭비하지 마십시오.

따라서 이러한 솔루션과 필요한 모든 회피책의 실장을 생각하기 전에, 자신이 수레바퀴를 다시 만들고 있다는 것을 깨닫고, 다른 사람이 이미 해결한 문제에 부딪히기 쉽다는 것을 깨달으십시오.

응용 프로그램에서 이미 jQuery를 사용하는 경우 자체 롤링 대신 테스트되고 유지 관리된 코드를 사용하고 이 모든 작업을 위해 타사 라이브러리를 사용하는 것이 좋습니다.

jquery.displays(댓글 중 @troseman에 의해 표시됨)는 폼이 변경되었는지 여부를 올바르게 검출하여 프롬프트 표시 중 사용자가 페이지를 이탈하지 않도록 하는 기능을 제공합니다.폼 리셋, 폼의 현재 상태 설정 등 기타 유용한 기능도 있습니다.사용 예:

$("#myForm").dirty({preventLeaving: true});

현재 포기된 오래된 프로젝트는 jQuery의 Are You Sure? 플러그인이며, 이 플러그인도 매우 효과적입니다. 데모 페이지를 참조하십시오.사용 예:

<script src="jquery.are-you-sure.js"></script>

<script>
  $(function() {
    $('#myForm').areYouSure(
      {
        message: 'It looks like you have been editing something. '
               + 'If you leave before saving, your changes will be lost.'
      }
    );
  });
  
</script>

사용자 지정 메시지는 지원되지 않습니다.

이미 2011년부터 Firefox 4에서는 이 대화 상자의 사용자 지정 메시지를 지원하지 않습니다.크롬51은 2016년 4월 현재 커스텀 메시지도 삭제되는 형태로 출시되고 있다.

이 사이트의 다른 곳에도 몇 가지 대안이 있지만, 저는 다음과 같은 대화 상자가 충분히 명확하다고 생각합니다.

이 사이트를 종료하시겠습니까?

변경한 내용은 저장되지 않을 수 있습니다.

Leave Stay

JavaScript on unload 이벤트를 확인합니다.Microsoft가 도입한 비표준 JavaScript이지만 대부분의 브라우저에서 동작하며 언로드문서에는 더 많은 정보와 예가 포함되어 있습니다.

jquery를 통해

$('#form').data('serialize',$('#form').serialize()); // On load save form current state

$(window).bind('beforeunload', function(e){
    if($('#form').serialize()!=$('#form').data('serialize'))return true;
    else e=null; // i.e; if form state change show warning box, else don't show it.
});

Google JQuery Form Serialize 기능을 사용하면 모든 양식 입력을 수집하여 배열에 저장할 수 있습니다.이 설명으로 충분할 것 같습니다:)

설정이 필요 없는 범용 솔루션.콘텐츠 가능한 요소를 포함한 모든 입력 변경을 자동으로 검출합니다.

"use strict";
(() => {
const modified_inputs = new Set;
const defaultValue = "defaultValue";
// store default values
addEventListener("beforeinput", (evt) => {
    const target = evt.target;
    if (!(defaultValue in target || defaultValue in target.dataset)) {
        target.dataset[defaultValue] = ("" + (target.value || target.textContent)).trim();
    }
});
// detect input modifications
addEventListener("input", (evt) => {
    const target = evt.target;
    let original;
    if (defaultValue in target) {
        original = target[defaultValue];
    } else {
        original = target.dataset[defaultValue];
    }
    if (original !== ("" + (target.value || target.textContent)).trim()) {
        if (!modified_inputs.has(target)) {
            modified_inputs.add(target);
        }
    } else if (modified_inputs.has(target)) {
        modified_inputs.delete(target);
    }
});
// clear modified inputs upon form submission
addEventListener("submit", (evt) => {
    modified_inputs.clear();
    // to prevent the warning from happening, it is advisable
    // that you clear your form controls back to their default
    // state with evt.target.reset() or form.reset() after submission
});
// warn before closing if any inputs are modified
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;
    }
});
})();

와심 A.의 뛰어난 연재 아이디어에 근거해 구축되었습니다.문제는 양식을 제출할 때 경고가 표시된다는 것입니다.이것은 여기에서 수정되었습니다.

var isSubmitting = false

$(document).ready(function () {
    $('form').submit(function(){
        isSubmitting = true
    })

    $('form').data('initial-state', $('form').serialize());

    $(window).on('beforeunload', function() {
        if (!isSubmitting && $('form').serialize() != $('form').data('initial-state')){
            return 'You have unsaved changes which will not be saved.'
        }
    });
})

Chrome 및 IE 11에서 테스트되었습니다.

이전 답변을 바탕으로 스택 오버플로우 내의 여러 장소에서 종합하여 작성한 솔루션을 다음에 제시하겠습니다.이 솔루션은 실제로 변경 내용을 제출하고 싶을 때 대처하는 것입니다.

window.thisPage = window.thisPage || {};
window.thisPage.isDirty = false;

window.thisPage.closeEditorWarning = function (event) {
    if (window.thisPage.isDirty)
        return 'It looks like you have been editing something' +
               ' - if you leave before saving, then your changes will be lost.'
    else
        return undefined;
};

$("form").on('keyup', 'textarea', // You can use input[type=text] here as well.
             function () { 
                 window.thisPage.isDirty = true; 
             });

$("form").submit(function () {
    QC.thisPage.isDirty = false;
});
window.onbeforeunload = window.thisPage.closeEditorWarning;

에서는 IE11에 IE11이 되고 있는 것 closeEditorWarning가 반환하다undefined이치노

다음 원라이너가 효과가 있었습니다.

window.onbeforeunload = s => modified ? "" : null;

★★★★★★★★★★를 설정해 주세요.modified응용 프로그램 상태에 따라 true 또는 false가 됩니다.

serialize()를 사용하여 폼 값을 시리얼화하여 URL 인코딩된 텍스트 문자열을 만들고 언로드 전에 폼이 변경되었는지 확인할 수 있습니다.

$(document).ready(function(){
    var form = $('#some-form'),
        original = form.serialize()

    form.submit(function(){
        window.onbeforeunload = null
    })

    window.onbeforeunload = function(){
        if (form.serialize() != original)
            return 'Are you sure you want to leave?'
    }
})

Vladimir Sidorenko가 작성한 https://coderwall.com/p/gny70a/alert-when-leaving-page-with-unsaved-form 링크를 참조하십시오.

다음 코드는 올바르게 동작합니다.ID 속성을 통해 양식 요소의 입력 변경에 도달해야 합니다.

var somethingChanged=false;
            $('#managerForm input').change(function() { 
                somethingChanged = true; 
           }); 
            $(window).bind('beforeunload', function(e){
                if(somethingChanged)
                    return "You made some changes and it's not saved?";
                else 
                    e=null; // i.e; if form state change show warning box, else don't show it.
            });
        });
var unsaved = false;
$(":input").change(function () {         
    unsaved = true;
});

function unloadPage() {         
    if (unsaved) {             
        alert("You have unsaved changes on this page. Do you want to leave this page and discard your changes or stay on this page?");
    }
} 
window.onbeforeunload = unloadPage;

Eli Grey의 유니버설 솔루션을 테스트하여 코드를 심플하게 한 후에야 동작했습니다.

  '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");
    }
  })();

간단한 답변:

let pageModified = true

window.addEventListener("beforeunload", 
  () => pageModified ? 'Close page without saving data?' : null
)

Eerik Sven Puudist의 솔루션...

var isSubmitting = false;

$(document).ready(function () {
    $('form').submit(function(){
        isSubmitting = true
    })

    $('form').data('initial-state', $('form').serialize());

    $(window).on('beforeunload', function() {
        if (!isSubmitting && $('form').serialize() != $('form').data('initial-state')){
            return 'You have unsaved changes which will not be saved.'
        }
    });
})

...복잡한 오브젝트 지향 환경에서 필요한 변경 없이 자발적으로 작업을 수행했습니다.

적용된 유일한 변경 사항은 "Form" ('Form' -> '#Form')이라는 구체적인 폼을 참조하는 것이었습니다.

<form ... id="formForm" name="formForm" ...>

특히, 송신 버튼이 「홀로 남겨져 있다」라고 하는 것은 좋은 일입니다.

또한 Firefox의 최신 버전(2019년 2월 7일 기준)에서도 사용할 수 있습니다.

@codecaster 아이디어에 추가하여 폼을 사용하여 모든 페이지에 추가할 수 있습니다(내 경우 글로벌하게 사용하기 때문에 폼에서만 이 경고가 발생합니다).기능을 다음과 같이 변경할 수 있습니다.

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

또한 로그인을 포함한 제출 양식과 취소 버튼 링크를 사용하여 사용자가 취소 또는 제출을 눌렀을 때 양식을 사용하는 모든 페이지에서도 경고가 트리거되지 않도록 하십시오.

<a class="btn btn-danger btn-md" href="back/url" onclick="setFormSubmitting()">Cancel</a>

자세한 설명은 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
    }

저는 Chrome에서만 테스트를 받고 다른 사람이 도움을 받을 수 있도록 여기서 공유했습니다.

변경 사항이 있는 경우에만 탭을 닫기 전에 사용자에게 경고하고 싶었습니다.

<input type="text" name="field" value="" class="onchange" />

var ischanged = false;

$('.onchange').change(function () {
    ischanged = true;
});

window.onbeforeunload = function (e) {
    if (ischanged) {
        return "Make sure to save all changes.";
    }        
};

그 때경고가 되었을 때, 많은 은, 에 on 가 발생하기 에, 「 」, 「 」등의 on submit 에서는 대응할입니다.폼을 송신했을 때 불필요한 경고가 표시되었을 때 회피책이 많았습니다.이것은, 송신전에 온로드가 기동하기 때문에, 온서밋 이벤트에서는 처리할 수 없기 때문입니다.onbeforeunload = null, 이벤트보다 했습니다.[ Submit 버튼이 울리기 때문에 코드를 갱신했습니다.

var isChanged = false;
var isSubmit = false;

window.onbeforeunload = function (e) {
    if (isChanged && (!isSubmit)) {
        return "Make sure to save all changes.";
    }        
};

$('#submitbutton').click(function () {
    isSubmit = true;
});

$('.onchange').change(function () {
    isChanged = true;
});

나는 다음과 같은 코드를 만들었다.모든 필드(.ignoreDirty 클래스로 표시된 필드 제외) 또는 현재 표시된 필드만 선택적으로 비교할 수 있습니다.Javascript에 의해 추가된 새로운 필드에 대해 재초기화할 수 있습니다.따라서 폼 상태가 아닌 각 컨트롤의 상태를 저장합니다.

/* Dirty warning for forms */
dirty = (skipHiddenOrNullToInit) => {
    /*  will return True if there are changes in form(s)
        for first initialization you can use both: .dirty(null) or .dirty() (ignore its result)
            .dirty(null) will (re)initialize all controls - in addititon use it after Save if you stay on same page
            .dirty() will initialize new controls - in addititon use it if you add new fields with JavaScript
        then
            .dirty() (or: .dirty(false)) says if data are changed without regard to hidden fields
            .dirty(true) says if data are changed with regard to hidden fields (ie. fields with .d-none or .hidden class)
        controls with .ignoreDirty class will be skipped always
        previous about .d-none, .hidden, .ignoreDirty applies to the control itself and all its ancestors
    */
    let isDirty = false;
    let skipSelectors = '.ignoreDirty';
    if (skipHiddenOrNullToInit) {
        skipSelectors += ', .d-none, .hidden'
    } else if (skipHiddenOrNullToInit === undefined) {
        skipHiddenOrNullToInit = false;
    }
    $('input, select').each(
    function(_idx, el) {
        if ($(el).prop('type') !== 'hidden') {
            let dirtyInit = $(el).data('dirty-init');
            if (skipHiddenOrNullToInit === null || dirtyInit === undefined) {
                try {
                    isChromeAutofillEl = $(el).is(":-webkit-autofill");
                } catch (error) {
                    isChromeAutofillEl = false;
                }
                if (isChromeAutofillEl && $(el).data('dirty-init') === undefined) {
                    setTimeout(function() {  // otherwise problem with Chrome autofilled controls
                        $(el).data('dirty-init', $(el).val());
                    }, 200)
                } else {
                    $(el).data('dirty-init', $(el).val());
                }
            } else if ($(el).closest(skipSelectors).length === 0 && dirtyInit !== $(el).val()) {
                isDirty = true;
                return false; // breaks jQuery .each
            }
        }
    }
    );
    return isDirty;
}

Chrome autofill 값은 초기화 및 로딩이 어려워서 추가로 문제가 있습니다.그래서 페이지 로드가 아닌 포커스인 이벤트에서는 초기화하지 않습니다.(그러나 자바스크립트에 의해 변경되는 컨트롤 값은 아직 문제가 있을 수 있습니다.페이지 로드 시 호출하는 코드는 다음과 같습니다.

let init_dirty = (ifStayFunc) => {
    /*  ifStayFunc: optional callback when user decides to stay on page
    use .clearDirty class to avoid warning on some button, however:
        if the button fires JavaScript do't use .clearDirty class and instead
            use directly dirty(null) in code - to be sure it will run before window.location */
    $('input, select').on('focusin', function(evt) {
        if (!$('body').data('dirty_initialized')) {
            dirty();
            $('body').data('dirty_initialized', true);
        }
    });
    window.addEventListener('beforeunload', (evt) => {
        if (dirty(true)) {
            if (ifStayFunc) {
                ifStayFunc();
            }
            evt.preventDefault();
            evt.returnValue = '';  // at least Google Chrome requires this
        }
    });
    $('.clearDirty').on('click', function(evt) {
        dirty(null);
    });
};

따라서 저장 버튼을 누르면 .clearDirty 클래스가 추가되므로 이 경우 경고를 방지할 수 있습니다.사용자가 경고를 받는 동안 페이지를 유지할 경우 ifStayFunc를 호출하여 작업을 수행할 수 있습니다.일반적으로 Save(저장) 버튼을 추가로 표시할 수 있습니다(기본/프라이머리 버튼만 표시되므로 Safe+SomethingMore가 됩니다).이 "SomethingMore"를 사용하여 저장을 허용합니다.

우선, 대부분의 브라우저는 기본적으로 이 기능을 가지고 있다.그리고 이게 왜 필요한 거죠?폼을 동기화하지 않는 이유는 무엇입니까?사용자의 제출을 기다리지 않고 변경 시 저장할 수 있습니다.Google 연락처처럼요.물론 양식의 모든 필드만 필수입니다.유저는, 필요한 것을 생각할 틈도 없이, 무언가를 억지로 가득 채우는 것을 좋아하지 않습니다. : )

언급URL : https://stackoverflow.com/questions/7317273/warn-user-before-leaving-web-page-with-unsaved-changes

반응형