Ludwig
Ludwig

Reputation: 1821

Javascript - prevent navigation during file upload

I have a vue component for video upload, where I am warning a user when he tries to navigate away during the video upload that he will lose the file if he does so, like this:

       ready() {
            window.onbeforeunload = () => {
                if (this.uploading && !this.uploadingComplete && !this.failed) {
                    this.confirm('Are you sure you want to navigate away? Your video won't be uploaded if you do so!');
                }
            }
        }

I am using sweetalert to alert the user about it. But how can I then make it stay on the same page, and prevent the navigation away before he confirms that he wants to navigate away?

This is the whole component:

<script>
    function initialState (){
        return {
            uid: null,
            uploading: false,
            uploadingComplete: false,
            failed: false,
            title: null,
            link: null,
            description: null,
            visibility: 'private',
            saveStatus: null,
            fileProgress: 0
        }
    }
    export default {
        data: function (){
            return initialState();
        },
        methods: {
            fileInputChange() {
                this.uploading = true;
                this.failed = false;

                this.file = document.getElementById('video').files[0];

                this.store().then(() => {
                    var form = new FormData();

                    form.append('video', this.file);
                    form.append('uid', this.uid);

                    this.$http.post('/upload', form, {
                        progress: (e) => {
                            if (e.lengthComputable) {
                                this.updateProgress(e)
                            }
                        }
                    }).then(() => {
                        this.uploadingComplete = true
                    }, () => {
                        this.failed = true
                    });
                }, () => {
                    this.failed = true
                })
            },
            store() {
                return this.$http.post('/videos', {
                    title: this.title,
                    description: this.description,
                    visibility: this.visibility,
                    extension: this.file.name.split('.').pop()
                }).then((response) => {
                    this.uid = response.json().data.uid;
                });
            },
            update() {
                this.saveStatus = 'Saving changes.';

                return this.$http.put('/videos/' + this.uid, {
                    link: this.link,
                    title: this.title,
                    description: this.description,
                    visibility: this.visibility
                }).then((response) => {
                    this.saveStatus = 'Changes saved.';

                    setTimeout(() => {
                        this.saveStatus = null
                    }, 3000)
                }, () => {
                    this.saveStatus = 'Failed to save changes.';
                });
            },
            updateProgress(e) {
                e.percent = (e.loaded / e.total) * 100;
                this.fileProgress = e.percent;
            },
            confirm(message) {
              swal({
                title: message,
                text: null,
                type: "warning",
                showCancelButton: true,
                cancelButtonText: "Cancel",
                cancelButtonColor: '#FFF',
                confirmButtonColor: "#2E112D",
                confirmButtonText: "Yes, delete"
              }).then(function(){
                  this.$data = initialState();
              }.bind(this), function(dismiss) {
                // dismiss can be 'overlay', 'cancel', 'close', 'esc', 'timer'
                if (dismiss === 'cancel') { // you might also handle 'close' or 'timer' if you used those
                // ignore
                } else {
                  throw dismiss;
                }
              })
            }
        },
        ready() {
            window.onbeforeunload = () => {
                if (this.uploading && !this.uploadingComplete && !this.failed) {
                    this.confirm('Are you sure you want to navigate away? Your video won't be uploaded if you do so!');
                }
            }
        }
    }
</script>

Upvotes: 2

Views: 850

Answers (2)

loa_in_
loa_in_

Reputation: 1011

Mozilla documentation suggests

window.onbeforeunload = function(e) {
  var dialogText = 'Dialog text here';
  e.returnValue = dialogText;
  return dialogText;
};

and also states that:

Since 25 May 2011, the HTML5 specification states that calls to window.alert(), window.confirm(), and window.prompt() methods may be ignored during this event. See the HTML5 specification for more details.

Source contains many other details regarding reasons and what to expect from modern browsers.

This question seems to be a duplicate of yours.

This answer suggests that to avoid weird browser behaviour you should set handler only when it's to prevent something (that is while navigating away should trigger a confirmation dialog)

Upvotes: 1

Rajshekar Reddy
Rajshekar Reddy

Reputation: 19007

But how can I then make it stay on the same page, and prevent the navigation away before he confirms that he wants to navigate away?

Add return false; to stop the event.

if (this.uploading && !this.uploadingComplete && !this.failed) {
      this.confirm("Are you sure you want to navigate away? Your video won't be uploaded if you do so!");
    return false;  // <==== add this
}

return false; does 3 separate things when you call it :

event.preventDefault(); – It stops the browsers default behaviour.

event.stopPropagation(); – It prevents the event from propagating (or “bubbling up”) the DOM.

Stops callback execution and returns immediately when called.

Upvotes: 0

Related Questions