【问题标题】:How to disable window.onbeforeunload on form submission with Vue?如何在使用 Vue 提交表单时禁用 window.onbeforeunload?
【发布时间】:2018-03-31 15:56:20
【问题描述】:

我有一个名为 countdowntimer.vue 的组件,它显然只是一个倒数计时器,我将它用于在线考试页面,我想在窗口对象上应用 onbeforeunload 事件,但我也希望计时器自动提交当它在没有被该窗口事件中断的情况下完成时,我尝试将代码放入 vuejs 组件中,但它只是没有响应,它要么不允许我在不中断的情况下提交,要么它根本不起作用并让任何事件都离开页面而不中断它。

这是倒数计时器的代码:

<template>
    <div>
        <div v-if="finished" v-text="expiredText"></div>

        <div v-else>
            <span>{{ remaining.minutes }} Minutes, </span>
            <span>{{ remaining.seconds }} Seconds</span>
            left...
        </div>
    </div>
</template>

<script>
    import moment from 'moment';
    export default {
        props: {
            until: { default: 600000},
            expiredText: { default: 'TIME IS UP' }
        },
        data () {
            return { limiter: this.until * 10000};
        },
        created () {
            this.refreshEverySecond();
            document.addEventListener('beforeunload', this.redirect());
        },
        computed: {
            finished () {
                return this.remaining.total <= 0;
            },
            remaining () {
                let remaining = moment.duration(this.limiter);
                if (remaining <= 0) this.$emit('finished');
                return {
                    total: remaining,
                    minutes: remaining.minutes(),
                    seconds: remaining.seconds()
                };
            },
        },
        methods: {
            refreshEverySecond () {
                let interval = setInterval(() => this.limiter = this.limiter - 1000, 1000);
                this.$on('finished', () => clearInterval(interval));
                this.$on('finished', () => this.timeUp());
            },

            timeUp() {
                const form = document.querySelector('[data-form-submit]');
                const radios = document.querySelectorAll('input[type=radio]');
                radios.forEach(radio => radio.style.display = 'none');
                form.submit(function(e) {
                    console.log(e);
                });
            },
            redirect () {
                // if(this.$on('finished')) {
                    // console.log(this.finished)
                    // window.onbeforeunload = function() {
                    //     return "Do you really want to leave our brilliant application?";
                    // };
                // }
                // console.log(this.finished())
                // return;
            }
        },
    }
</script>

我尝试将方法设置为计算属性和具有不同 if 语句的观察者,但它就像我上面提到的那样不起作用。

这是我正在使用的刀片模板

@extends('layouts.app')

@section('content')
    <div class="container">
        <div class="row">
            <div class="col-md-6 col-md-offset-3">
                <form method="POST" onsubmit="clicked()" data-form-submit>
                    {{ csrf_field() }}
                    <div class="panel panel-danger">
                        <div class="panel-heading">
                            {{ $quiz->quiz_name }}
                        </div>
                        <div class="panel-body">
                            <ol>
                                @foreach($quiz->questions as $index => $question)
                                    <li><h4>{{ $question->title }} ? </h4></li>
                                    <ul>
                                        <div class="flex-align-baseline">
                                            <li>{{$question->option_1}}</li>
                                            <input type="radio" name="{{$index}}" value="{{ $question->option_1 }}">
                                            <hr>
                                        </div>
                                        <div class="flex-align-baseline">
                                            <li>{{$question->option_2}}</li>
                                            <input type="radio" name="{{$index}}" value="{{ $question->option_2 }}">
                                            <hr>
                                        </div>
                                        <div class="flex-align-baseline">
                                            <li>{{$question->option_3}}</li>
                                            <input type="radio" name="{{$index}}" value="{{ $question->option_3 }}">
                                            <hr>
                                        </div>
                                        <div class="flex-align-baseline">
                                            <li>{{$question->option_4}}</li>
                                            <input type="radio" name="{{$index}}" value="{{ $question->option_4 }}">
                                            <hr>
                                        </div>
                                    </ul>
                                    <hr>
                                @endforeach
                            </ol>
                            <input type="submit" class="btn btn-success" onclick="clicked()" value="Submit">
                        </div>
                    </div>
                </form>
            </div>
            <timer :until="{{ count($quiz->questions) }}" class="countdown col-md-3"></timer>
        </div>
    </div>

@endsection

<script>

let submitForm = false;
function clicked() {
    submitForm = true;
}
window.onbeforeunload = function(e) {
    if (submitForm) {
        return null;
    }
    return "Do you really want to leave our brilliant application?";
};
</script>

如您所见,我在@endsection 之外有一个脚本标签,我了解到您可能无法这样做,它不会连接到刀片模板本身的任何元素,我试过了像我在 vue 组件中那样抓取表单对象,但它返回 null 或 undefined 我不记得了,你不能将事件侦听器附加到 undefined,但是如果我在浏览器的控制台中运行相同的逻辑它按预期工作,我在表单上的onsubmit="" 事件由于某种原因没有到达底部的那些脚本标签,submitForm 变量的值没有改变,但是如果我点击了就够奇怪了手动提交按钮确实触发了函数clicked(),所以我在这里很困惑,我不知道我是否可以用vue来实现这一点,如果不是我不知道为什么onsubmit=""事件不工作,当然我不能在@section 中移动脚本标签,因为 vue 会发出尖叫声,如果你知道我应该怎么做这段代码我将不胜感激。

【问题讨论】:

    标签: javascript laravel vue.js vuejs2


    【解决方案1】:

    首先,您应该将方法引用传递给beforeunload,而不是调用该方法的结果。所以,删除():

    created () {
        this.refreshEverySecond();
        document.addEventListener('beforeunload', this.redirect); // not this.redirect()
    },
    

    现在,启用/禁用处理程序的简单解决方案是添加一个标志:

        data () {
            return {
                limiter: this.until * 10000
                preventSubmit: true
            };
        },
    

    并且,在您的方法中,更新/使用该标志:

        methods: {
            // ...
            timeUp() {
                this.preventSubmit = false; // ALLOW redirect now
    
                const form = document.querySelector('[data-form-submit]');
                const radios = document.querySelectorAll('input[type=radio]');
                radios.forEach(radio => radio.style.display = 'none');
                form.submit(function(e) {
                    console.log(e);
                });
            },
            redirect () {
                if (this.preventSubmit) {
                   // do your thing to prevent submit
                }
            }
        },
    

    另类

    或者,您可以删除监听器:

    created () {
        this.refreshEverySecond();
        document.addEventListener('beforeunload', this.redirect); // not this.redirect()
    },
    

    还有:

    methods: {
        // ...
        timeUp() {
            document.removeEventListener('beforeunload', this.redirect);
            // ...
    

    但我认为标志替代方案更安全。


    正确处理 unbeforeunload

    每个 cmets,我正在添加一个演示它是如何工作的。

    请参阅JSFiddle DEMO here 或下面的演示。

    new Vue({
      el: '#app',
      data: {
        preventSubmit : true
      },
      mounted () {
        window.addEventListener("beforeunload", this.redirect);
    	},
      methods: {
      	redirect(event) {
        	if (this.preventSubmit) {
    		  	var confirmationMessage = "\o/";
      			event.returnValue = confirmationMessage;     // Gecko, Trident, Chrome 34+
      			return confirmationMessage;              // Gecko, WebKit, Chrome <34
          }
        }
      }
    })
    <script src="https://unpkg.com/vue"></script>
    
    <div id="app">
      <p>preventSubmit ? {{ preventSubmit  }}</p>
      <button @click="preventSubmit = !preventSubmit ">Toggle preventSubmit </button>
    </div>
    <br>
    <a href="/somewhere-else">click to try to navigate away</a>

    【讨论】:

    • 现在有一个问题,文档事件侦听器“beforeunload”没有触发,因此禁止我正确测试,我不知道为什么事件侦听器没有捕获该卸载事件,任何想法?
    • 导航离开不是触发它吗?
    • 不,即使将“文档”更改为“窗口”仍然没有,但是当然如果我更改了我正在听的事件,比如说点击,一切都会正常触发。
    猜你喜欢
    • 2015-01-06
    • 2012-03-30
    • 2022-01-11
    • 2016-04-03
    • 1970-01-01
    • 2017-10-10
    • 1970-01-01
    • 2018-06-30
    • 1970-01-01
    相关资源
    最近更新 更多