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


342

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

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



3
На этот вопрос здесь должен быть ответ, который вам
— нужен


Ответы:


557

Короткий, неправильный ответ:

Вы можете сделать это, обработав beforeunloadсобытие и вернув ненулевую строку :

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.
});

Проблема этого подхода заключается в том, что отправка формы также запускает событие unload . Это легко исправить, добавив флаг, которым вы отправляете форму:

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события в сочетании с «грязным» флагом, который вызывает запрос только в том случае, если он действительно актуален.

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()будет работать), но самая большая проблема заключается в том, что jQuery serialize()будет работать только с именованными, не отключенными элементами, поэтому изменение любого отключенного или безымянного элемента не вызовет грязный флаг. Для этого есть обходные пути , такие как создание элементов управления только для чтения вместо включения, сериализации и повторного отключения элементов управления.

Таким образом, события, кажется, путь. Вы можете попробовать прослушивать нажатия клавиш . Это событие имеет несколько проблем:

  • Не будет вызывать флажки, переключатели или другие элементы, которые изменяются с помощью мыши.
  • Сработает для нерелевантных нажатий клавиш, таких как Ctrlклавиша.
  • Не будет срабатывать при значениях, установленных через код JavaScript.
  • Не будет запускаться при вырезании или вставке текста через контекстные меню.
  • Не будет работать для виртуальных входов, таких как указатели даты или флажки / кнопки-переключатели, которые сохраняют свои значения в скрытом вводе через JavaScript.

changeСобытие также не вызывает на значениях , установленных в коде JavaScript , так и не будет работать для виртуальных входов.

Привязка inputсобытия ко всем inputs (и textareas и selects) на вашей странице не будет работать в старых браузерах и, как и все упомянутые выше решения по обработке событий, не поддерживает отмену. Когда пользователь изменяет текстовое поле, а затем отменяет его, или устанавливает и снимает флажок, форма все еще считается грязной.

А когда вы захотите реализовать больше поведения, например игнорировать определенные элементы, у вас будет еще больше работы.

Не изобретай велосипед

Поэтому, прежде чем задуматься о реализации этих решений и всех необходимых обходных путей, поймите, что вы заново изобретаете колесо и вы склонны сталкиваться с проблемами, которые другие для вас уже решили.

Если ваше приложение уже использует jQuery, вы также можете использовать проверенный, поддерживаемый код вместо собственного и использовать библиотеку третьей стороны для всего этого. JQuery вы уверены? Плагин прекрасно работает, смотрите их демонстрационную страницу . Это так просто, как это:

<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>

Пользовательские сообщения не поддерживаются везде

Обратите внимание, что Firefox 4 не поддерживает пользовательские сообщения в этом диалоговом окне. По состоянию на апрель 2016 года развернут Chrome 51, в котором также удаляются пользовательские сообщения .

На этом сайте есть другие альтернативы, но я думаю, что такой диалог достаточно ясен:

Вы хотите покинуть этот сайт?

Внесенные вами изменения не могут быть сохранены.

Leave Stay


6
Обратите внимание, что использование пользовательских строк больше не работает в Chrome; chromestatus.com/feature/5349061406228480
— Amann

6
Да, это описано в последнем разделе моего ответа.
— CodeCaster

2
@ Крис, потому что Angular манипулирует DOM, чтобы изменить страницу, не отходя от текущего документа.
— CodeCaster

15
Имейте в виду, что jQuery areYouSure был оставлен с 2014 года (57 открытых выпусков на Github ), он полон ошибок из моего опыта, может показаться, что он работает сначала, но после некоторых быстрых тестов я понял, что это не всегда работает. Не рекомендовал бы это вообще
— Марк

4
@JacopoStanchi Я, к сожалению, не нашел ни одного. И я имею в виду, я выглядел довольно много - но это было еще в 10 января, поэтому кто-то, возможно, написал что-то, но я сомневаюсь в этом. Лучше всего реализовать это самостоятельно - это не то, что вы хотите услышать, так что извините. (Но, по крайней мере, вы не получите никаких сюрпризов, как я использовал jQuery areYouSure.) Кроме того, кто знает, может быть, вы даже можете сделать его открытым исходным кодом, и ваша реализация будет доступна другим;)
— Марк

75

Проверьте событие JavaScript onbeforeunload . Это нестандартный JavaScript, представленный Microsoft, однако он работает в большинстве браузеров, и их документация onbeforeunload содержит больше информации и примеров.


3
Я думаю, что теперь это стало стандартом. >> Мероприятие было впервые представлено Microsoft в Internet Explorer 4 и стандартизировано в спецификации HTML5.
— ви

34

через 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, которая соберет все входные данные формы и сохранит их в массиве. Я думаю, этого объяснения достаточно :)


6
не используйте #form в качестве идентификатора формы, потому что окно создаст объект с именем form :)
— mdikici

1
Проверьте здесь, если это не работает для вас: stackoverflow.com/questions/7080269/…
— Leniel Maccaferri

16

Универсальное решение, не требующее настройки, которое автоматически обнаруживает все входные изменения, включая contenteditable элементы:

"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", () => {
    modified_inputs.clear();
    // to prevent the warning from happening, it is advisable
    // that you clear your form controls back to their default
    // state 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;
    }
});
})();

1
Холодные бобы. Просто скопируйте и вставьте этот код на тестовую страницу, внесите изменения и нажмите «Обновить». Он отобразит собственное предупреждение браузера, предупреждающее вас о том, что у вас есть несохраненные изменения.
— TheLegendaryCopyCoder

1
Это сработало для меня на Chrome 71.0.3578.98 (Официальная сборка) (64-битная
— версия

2
Это работает очень хорошо, но если вы используете это с формой для отправки, вам нужно будет сначала очистить установленный модифицированную_вводу, прежде чем вы получите предупреждение о подтверждении изменений. Я добавил прослушиватель событий в мою форму для вызова при отправке и просто запустил функцию set clear, таким образом, модифицированное значение input очищается при запуске прослушивателя событий перед загрузкой и позволяет продолжать публикацию.
— stepquick

1
Круто на самом деле копировать и вставлять. спасибо, что сэкономил мое время. Работа для меня в Firfox 75.0
— Connectify_user

1
Люблю этот фрагмент
— Rawburner

10

Построенный на основе отличной идеи Васима А. использовать сериализацию. Проблема заключалась в том, что предупреждение также отображалось при отправке формы. Это было исправлено здесь.

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.


Wasim A и Eerik Sven Puudist - Спасибо, я использовал id для формы и id для своей кнопки сохранения, так как все мои кнопки были типа submit <form action = "" id = "marksform" method = "POST"> <td> <input type = "submit" name = "submit_button" id = "save" value = "Save"> </ td> <td> <input type = "submit" name = "submit_button" value = "Next"> </ td> и несколько более всех типов Представлять Поэтому заменить .Submit (специфическими кнопкой мыши $ ( '# сохранить') нажмите кнопку (функция () {isSubmitting = True}) используйте '#marksform' вместо 'формы'.
— Jayanta

7

Основываясь на предыдущих ответах и ​​собранных вместе из разных мест переполнения стека, вот решение, которое я придумал, которое обрабатывает случай, когда вы действительно хотите отправить свои изменения:

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, похоже, требует, чтобы closeEditorWarningфункция возвращалась, undefinedчтобы не показывать предупреждение.


Это решение не проверяет, изменяются ли какие-либо значения по сравнению с их исходным состоянием, поэтому становится недействительным в случае, если пользователь вводит текст и удаляет введенный текст, прежде чем пытаться уйти.
— Бретт Вебер

Хороший вопрос, @BrettWeber. В этом случае вы можете изменить closeEditorWarning и иметь «if (window.thisPage.isDirty && uiHasChanged ())», где uiHasChanged - это функция, которая знает об исходном состоянии от сервера и проверяет все различия, но это решение было для случай, когда я не особо хотел выполнять всю эту дополнительную работу, и это дает ложные срабатывания только тогда, когда в текстовых областях были сделаны реальные нажатия клавиш, что, по моему мнению, было разумным компромиссом. :)
— Джонатан

Не должно QC.thisPage.isDirty = false;быть window.thisPage.isDirty = false;? Таким образом, он проверит, отправляете ли вы форму, и не отобразит предупреждение. Казалось, работает для моих тестов. Кроме того, большинство форм имеют, inputа не textarea. Я использовал следующее, что работает довольно хорошо:$("form").on('keyup', 'textarea,input,select', function () {
— Майк

7

Следующая однострочная работа сработала для меня.

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

Просто установите modifiedв истинно или ложно в зависимости от состояния приложения.


2
Некоторые предложения: Возвращайте пользовательское сообщение, а не пустую строку, так как некоторые браузеры будут отображать его (например, Edge), и возвращайте, undefinedа не nullкак IE, выведет 'null', если modifiedустановлено значение false.
— Кевин Ли

1
как насчет мобильных устройств? Насколько я понимаю, IOS-устройства не работают с предзагрузкой.
— Джейбро

4

Следующий код прекрасно работает. Вам нужно достичь изменений ввода элементов формы через атрибут 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.
            });
        });

3
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;

Я вставил его в отдельный файл и включил в <script src = "warnOnChange.js"> </ script>, но ничего не происходит, когда я изменяю текстовое поле и затем выполняю команду заголовка.
— Фабрицио Бартоломуччи

2

Вы можете использовать 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?'
    }
})

Перейдите по этой ссылке https://coderwall.com/p/gny70a/alert-when-leaving-page-with-unsaved-form Автор сценария Владимир Сидоренко


1
Это сработало, но, как ответила разработчик кода, у этого метода есть некоторые недостатки, см. Это объяснение stackoverflow.com/a/7317311/10555981
— Pradeep

Уже есть пара использованных ответов serialize(), и, действительно, у этого есть свои недостатки.
— CodeCaster

Это должен быть правильный ответ, очень простой в использовании и работающий как положено.
— Jodyshop

1

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

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

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

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

1

Вы можете проверить подробное объяснение здесь: 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

Решение от 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.'
        }
    });
})

... спонтанно выполнил эту работу для меня в сложной объектно-ориентированной обстановке без каких-либо изменений.

Единственное изменение, которое я применил, состояло в том, чтобы ссылаться на конкретную форму (только одну форму на файл), называемую «formForm» («form» -> «#formForm»):

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

Особенно хорошо сделано то, что кнопка отправки «оставлена ​​в покое».

Кроме того, он работает и для меня с последней версией Firefox (по состоянию на 7 февраля 2019 года).


1

Протестировал универсальное решение 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");
    }
  })();

0

Я сделал это по-другому, поделившись здесь, чтобы кто-то мог получить помощь, протестирован только с 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.";
    }        
};

Работает хорошо, но есть другая проблема: когда я отправляю форму, я получаю нежелательное предупреждение, я вижу много обходных путей, это происходит потому, что onbeforeunload срабатывает перед отправкой, поэтому мы не можем обработать его в событии onsubmit, как onbeforeunload = null, но Событие onclick кнопки отправки срабатывает перед обоими этими событиями, поэтому я обновил код

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;
});

-5

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


Будете ли вы правы, но мои формы генерируются динамически, и поля не находятся под моим контролем, поэтому для меня невозможно отследить все поля и отправить запрос через ajax, если вы что-то имеете в виду, пожалуйста, поделитесь со мной.
— Хан

Ладно, это может не понадобиться для ваших целей, но все же может быть реализовано. Если элементы формы находятся на экране, вы можете прикрепить свои собственные обработчики событий и получить любые данные, которые вы хотите. Затем используйте AJAX для сохранения данных в любом месте. Даже если формы имеют случайно сгенерированные идентификаторы или имена классов, вы можете использовать XPath, если можете предвидеть структуру формы. Это правильно XML / HTML? Таким образом, вы могли бы даже иметь JAD с людьми, ответственными за динамически генерируемую форму, и поговорить о том, как XSTL может помочь вам обоим в этом проекте. Просто мысль ...
— SimonDever

1
save it on any change without waiting any submitting from userВы не всегда хотите это делать. Иногда пользователь хочет внести много изменений, а затем просмотреть и подтвердить, что он хочет сохранить его.
— BadHorsie

1
Я не предлагал отправить форму. Данные могут быть сохранены в localStorage, в некотором кеше или даже представлены с каким-либо флагом черновика. Есть много способов для этого поведения. Это намного лучше, чем предупреждать пользователя о том, что он не заполнил форму, и если он не захочет закрыть окно, ему придется заполнить его снова в следующий раз. «Давай, мужик, не ленись, твой поезд / автобус / самолет / и т. Д. Не имеет значения, а также нам все равно, что ты забыл зарядное устройство со своего ноутбука, просто заполни эту чертову форму!»
— YuS
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.