Ngăn chặn việc gửi hai biểu mẫu trong jQuery


170

Tôi có một hình thức mất một chút thời gian để máy chủ xử lý. Tôi cần đảm bảo rằng người dùng chờ đợi và không cố gửi lại biểu mẫu bằng cách nhấp vào nút một lần nữa. Tôi đã thử sử dụng mã jQuery sau đây:

<script type="text/javascript">
$(document).ready(function() {
    $("form#my_form").submit(function() {
        $('input').attr('disabled', 'disabled');
        $('a').attr('disabled', 'disabled');
        return true;
    });
});
</script>

Khi tôi thử điều này trong Firefox, mọi thứ đều bị vô hiệu hóa nhưng biểu mẫu không được gửi với bất kỳ dữ liệu POST nào mà nó được cho là bao gồm. Tôi không thể sử dụng jQuery để gửi biểu mẫu vì tôi cần nút được gửi cùng với biểu mẫu vì có nhiều nút gửi và tôi xác định giá trị nào được sử dụng bởi giá trị nào được đưa vào POST. Tôi cần mẫu đơn được gửi như thường lệ và tôi cần phải vô hiệu hóa mọi thứ ngay sau khi điều đó xảy ra.

Cảm ơn!


Cảm ơn tất cả mọi người vì sự giúp đỡ của các bạn!
Adam

Câu trả lời:


315

Cập nhật năm 2018 : Tôi vừa nhận được một số điểm cho câu trả lời cũ này và chỉ muốn thêm vào đó là tốt nhất giải pháp sẽ là làm cho hoạt động trở nên bình thường để các bài nộp trùng lặp là vô hại.

Ví dụ: nếu biểu mẫu tạo đơn hàng, hãy đặt ID duy nhất vào biểu mẫu. Lần đầu tiên máy chủ nhìn thấy yêu cầu tạo đơn hàng với id đó, nó sẽ tạo nó và trả lời "thành công". Những lần gửi tiếp theo cũng sẽ đáp ứng "thành công" (trong trường hợp khách hàng không nhận được phản hồi đầu tiên) nhưng không nên thay đổi bất cứ điều gì.

Các bản sao phải được phát hiện thông qua kiểm tra tính duy nhất trong cơ sở dữ liệu để ngăn chặn các điều kiện chủng tộc.


Tôi nghĩ rằng vấn đề của bạn là dòng này:

$('input').attr('disabled','disabled');

Bạn đang vô hiệu hóa TẤT CẢ các đầu vào, bao gồm, tôi đoán, những dữ liệu mà biểu mẫu có nghĩa vụ phải gửi.

Để vô hiệu hóa chỉ các nút gửi, bạn có thể làm điều này:

$('button[type=submit], input[type=submit]').prop('disabled',true);

Tuy nhiên, tôi không nghĩ IE sẽ gửi biểu mẫu nếu ngay cả những nút đó bị tắt. Tôi muốn đề xuất một cách tiếp cận khác.

Một plugin jQuery để giải quyết nó

Chúng tôi chỉ giải quyết vấn đề này với mã sau đây. Mẹo ở đây là sử dụng jQuery data()để đánh dấu biểu mẫu là đã gửi hay chưa. Bằng cách đó, chúng ta không phải gặp rắc rối với các nút gửi, điều này làm cho IE hoảng sợ.

// jQuery plugin to prevent double submission of forms
jQuery.fn.preventDoubleSubmission = function() {
  $(this).on('submit',function(e){
    var $form = $(this);

    if ($form.data('submitted') === true) {
      // Previously submitted - don't submit again
      e.preventDefault();
    } else {
      // Mark it so that the next submit can be ignored
      $form.data('submitted', true);
    }
  });

  // Keep chainability
  return this;
};

Sử dụng nó như thế này:

$('form').preventDoubleSubmission();

Nếu có hình thức AJAX mà nên được phép gửi nhiều lần mỗi tải trang, bạn có thể cung cấp cho họ một lớp học chỉ ra rằng, sau đó loại trừ chúng từ chọn của bạn như thế này:

$('form:not(.js-allow-double-submission)').preventDoubleSubmission();

2
Chỉ là một con trỏ - sẽ tốt hơn nếu lưu cache $(this)vào a var that = $(this)?
Stuart.Sklinar

5
@ Stuart.Sklinar - ý kiến ​​hay. $formMặc dù vậy, tôi đã sử dụng - 'biểu mẫu' vì nó mô tả nhiều hơn và dẫn đầu $bởi vì theo quy ước của tôi, điều đó có nghĩa đó là một đối tượng jQuery.
Nathan Long

21
Khi sử dụng jquery xác nhận hợp lệ xác thực không phô trương tốt hơn để kiểm tra xem biểu mẫu có hợp lệ không. if ($ (this) .valid)) {$ form.data ('đã gửi', đúng);}
cck

1
@cck Tuyệt vời, tôi đã sử dụng nó. Bạn đã có một paren đóng thêm mà tôi đã xóa: if ($ (this) .valid) {$ form.data ('submit', true);
Brian MacKay

1
@BrianMacKay nếu biểu mẫu không hợp lệ và nút gửi được nhấn nút plugin jquery này thuộc tính được gửi đúng nhưng xác thực không phô trương sẽ ngăn việc gửi do lỗi xác thực. Sau khi sửa lỗi, người ta không thể gửi biểu mẫu vì thuộc tính đã gửi là đúng!
cck

44

Cách tiếp cận thời gian là sai - làm thế nào để bạn biết hành động sẽ diễn ra trong bao lâu trên trình duyệt của khách hàng?

Làm thế nào để làm nó

$('form').submit(function(){
  $(this).find(':submit').attr('disabled','disabled');
});

Khi biểu mẫu được gửi, nó sẽ vô hiệu hóa tất cả các nút gửi bên trong.

Hãy nhớ rằng, trong Firefox khi bạn tắt một nút, trạng thái này sẽ được ghi nhớ khi bạn quay lại lịch sử. Ví dụ, để ngăn chặn việc bạn phải bật các nút khi tải trang.


2
+1 vì mẹo firefox, nhưng có tùy chọn nào khác ngoài việc bật các nút khi tải trang không?
Raphael Isidro

1
Đây là cách sạch nhất để đi. Tôi đã cố gắng vô hiệu hóa nút gửi bằng trình xử lý sự kiện nhấp nhưng cách tiếp cận của tôi đã gây ra sự cố với chrome. Giải pháp này hoạt động rất đẹp.
Anthony đến

1
Hãy cẩn thận với phương pháp này, nếu bạn có một giá trị trên nút gửi và bạn cần nó, biểu mẫu sẽ không gửi nó.
Fabien Ménager

Theo tài liệu jQuery mới nhất, bạn nên sử dụng .prop thay vì .attr. Tham khảo: api.jquery.com/prop/#entry-longdesc-1 "Phương thức .prop () nên được sử dụng để đặt bị vô hiệu hóa và được kiểm tra thay vì phương thức .attr ()."
J Plana

19

Tôi nghĩ câu trả lời của Nathan Long là con đường để đi. Đối với tôi, tôi đang sử dụng xác thực phía máy khách, vì vậy tôi chỉ cần thêm một điều kiện là biểu mẫu hợp lệ.

EDIT : Nếu điều này không được thêm vào, người dùng sẽ không bao giờ có thể gửi biểu mẫu nếu xác thực phía máy khách gặp lỗi.

        // jQuery plugin to prevent double submission of forms
        jQuery.fn.preventDoubleSubmission = function () {
            $(this).on('submit', function (e) {
                var $form = $(this);

                if ($form.data('submitted') === true) {
                    // Previously submitted - don't submit again
                    alert('Form already submitted. Please wait.');
                    e.preventDefault();
                } else {
                    // Mark it so that the next submit can be ignored
                    // ADDED requirement that form be valid
                    if($form.valid()) {
                        $form.data('submitted', true);
                    }
                }
            });

            // Keep chainability
            return this;
        };

9

event.timeStampkhông hoạt động trong Firefox. Trả lại sai là không chuẩn, bạn nên gọi event.preventDefault(). Và trong khi chúng ta đang ở đó, luôn luôn sử dụng dấu ngoặc nhọn với cấu trúc điều khiển .

Để tổng hợp tất cả các câu trả lời trước đây, đây là một plugin thực hiện công việc và hoạt động trên nhiều trình duyệt.

jQuery.fn.preventDoubleSubmission = function() {

    var last_clicked, time_since_clicked;

    jQuery(this).bind('submit', function(event) {

        if(last_clicked) {
            time_since_clicked = jQuery.now() - last_clicked;
        }

        last_clicked = jQuery.now();

        if(time_since_clicked < 2000) {
            // Blocking form submit because it was too soon after the last submit.
            event.preventDefault();
        }

        return true;
    });
};

Để giải quyết Kern3l, phương pháp thời gian hoạt động với tôi chỉ đơn giản vì chúng tôi đang cố gắng dừng nhấp đúp vào nút gửi. Nếu bạn có thời gian phản hồi rất lâu đối với việc gửi, tôi khuyên bạn nên thay thế nút gửi hoặc biểu mẫu bằng một công cụ quay vòng.

Hoàn toàn chặn các lần gửi tiếp theo của biểu mẫu, như hầu hết các ví dụ trên, có một tác dụng phụ xấu: nếu có lỗi mạng và họ muốn thử gửi lại, họ sẽ không thể làm như vậy và sẽ mất các thay đổi. thực hiện. Điều này chắc chắn sẽ làm cho một người dùng tức giận.


2
Bạn đúng. Bạn có thể đi theo một cách khác - vô hiệu hóa ngay lập tức và sau đó, sau một khoảng thời gian dài, bật lại. Ngăn chặn nhấp đúp, cho phép gửi lại, nhưng vẫn cho phép gửi lại không hợp lệ cho người dùng bị lag.
Ctrl-C

8

Xin vui lòng, kiểm tra jquery-safeform plugin .

Ví dụ sử dụng:

$('.safeform').safeform({
    timeout: 5000,  // disable next submission for 5 sec
    submit: function() {
        // You can put validation and ajax stuff here...

        // When done no need to wait for timeout, re-enable the form ASAP
        $(this).safeform('complete');
        return false;
    }
});

4

... nhưng biểu mẫu không được gửi cùng với bất kỳ dữ liệu POST nào được cho là bao gồm.

Chính xác. Tên / giá trị của phần tử biểu mẫu bị vô hiệu hóa sẽ không được gửi đến máy chủ. Bạn nên đặt chúng là các yếu tố chỉ đọc .

Ngoài ra, neo không thể bị vô hiệu hóa như thế. Bạn sẽ cần xóa HREF của họ (không được khuyến nghị) hoặc ngăn hành vi mặc định của họ (cách tốt hơn), ví dụ:

<script type="text/javascript">
$(document).ready(function(){
    $("form#my_form").submit(function(){
      $('input').attr('readonly', true);
      $('input[type=submit]').attr("disabled", "disabled");
      $('a').unbind("click").click(function(e) {
          e.preventDefault();
          // or return false;
      });
    });
</script>

@Adam, vâng, bất kỳ trình xử lý nhấp chuột đính kèm sẽ bắn. Tôi nghĩ bạn chỉ muốn ngăn chặn chúng được sửa đổi?
karim79

@ karim79 Tôi không quan tâm nếu trình xử lý nhấp chuột bị sa thải, tôi chỉ muốn đảm bảo rằng biểu mẫu không được gửi lại bởi người dùng nhấp vào nút gửi khác.
Adam

@Adam - không, điều đó sẽ không xảy ra. Tôi lấy ví dụ của tôi, tôi đã thêm $('input[type=submit]').attr("disabled", "disabled");nhưng cách yêu thích của tôi là đưa onclick="this.disabled = 'disabled'"vào onclick của người gửi.
karim79

@ karim79 Tôi đã thử điều đó, nhưng sau đó nút gửi tôi đang nhấp không được bao gồm trong POST. Tôi cần đưa nó vào POST để xác định nút nào được nhấp vào
Adam

Tôi biết rằng bạn chỉ bỏ qua sự kiện nhấp chuột cho 'a' nhưng tại sao không sử dụng $ ('a'). Click (function (e) {e.preventDefault (); // hoặc trả về false;}); ?
Fabio

4

Có khả năng cải thiện cách tiếp cận của Nathan Long . Bạn có thể thay thế logic để phát hiện biểu mẫu đã gửi bằng biểu mẫu này:

var lastTime = $(this).data("lastSubmitTime");
if (lastTime && typeof lastTime === "object") {
    var now = new Date();
    if ((now - lastTime) > 2000) // 2000ms
        return true;
    else
        return false;
}
$(this).data("lastSubmitTime", new Date());
return true; // or do an ajax call or smth else

1
Tại sao trên trái đất bạn sẽ sử dụng một bộ đếm thời gian cho phương pháp này?
Bobort

4

Mã của Nathan nhưng dành cho plugin Xác thực jQuery

Nếu bạn tình cờ sử dụng plugin Xác thực jQuery, họ đã triển khai trình xử lý và trong trường hợp đó, không có lý do gì để thực hiện nhiều hơn một. Mật mã:

jQuery.validator.setDefaults({
  submitHandler: function(form){
    // Prevent double submit
    if($(form).data('submitted')===true){
      // Previously submitted - don't submit again
      return false;
    } else {
      // Mark form as 'submitted' so that the next submit can be ignored
      $(form).data('submitted', true);
      return true;
    }
  }
});

Bạn có thể dễ dàng mở rộng nó trong } else {-block để tắt đầu vào và / hoặc nút gửi.

Chúc mừng


2

Tôi đã kết thúc bằng cách sử dụng các ý tưởng từ bài đăng này để đưa ra một giải pháp khá giống với phiên bản của AtZako.

 jQuery.fn.preventDoubleSubmission = function() {

    var last_clicked, time_since_clicked;

    $(this).bind('submit', function(event){

    if(last_clicked) 
      time_since_clicked = event.timeStamp - last_clicked;

    last_clicked = event.timeStamp;

    if(time_since_clicked < 2000)
      return false;

    return true;
  });   
};

Sử dụng như thế này:

$('#my-form').preventDoubleSubmission();

Tôi thấy rằng các giải pháp không bao gồm một số loại thời gian chờ mà chỉ bị vô hiệu hóa trình gửi hoặc các thành phần biểu mẫu bị vô hiệu hóa gây ra sự cố vì một khi khóa được kích hoạt, bạn không thể gửi lại cho đến khi bạn làm mới trang. Điều đó gây ra một số vấn đề cho tôi khi làm công cụ ajax.

Điều này có lẽ có thể được nâng lên một chút vì nó không phải là ưa thích.


2

Nếu sử dụng AJAX để đăng một biểu mẫu, bộ async: falsesẽ ngăn các lần gửi bổ sung trước khi biểu mẫu xóa:

$("#form").submit(function(){
    var one = $("#one").val();
    var two = $("#two").val();
    $.ajax({
      type: "POST",
      async: false,  // <------ Will complete submit before allowing further action
      url: "process.php",
      data: "one="+one+"&two="+two+"&add=true",
      success: function(result){
        console.log(result);
        // do something with result
      },
      error: function(){alert('Error!')}
    });
    return false;
   }
});

3
Đặt <code> asnyc: true </ code> là một cách tiếp cận tồi vì nó đóng băng trình duyệt cho đến khi yêu cầu hoàn tất, đánh bại bản chất của AJAX
Edwin M

2

Đã sửa đổi giải pháp của Nathan một chút cho Bootstrap 3. Điều này sẽ đặt văn bản tải thành nút gửi. Ngoài ra, nó sẽ hết thời gian chờ sau 30 giây và cho phép biểu mẫu được gửi lại.

jQuery.fn.preventDoubleSubmission = function() {
  $('input[type="submit"]').data('loading-text', 'Loading...');

  $(this).on('submit',function(e){
    var $form = $(this);

    $('input[type="submit"]', $form).button('loading');

    if ($form.data('submitted') === true) {
      // Previously submitted - don't submit again
      e.preventDefault();
    } else {
      // Mark it so that the next submit can be ignored
      $form.data('submitted', true);
      $form.setFormTimeout();
    }
  });

  // Keep chainability
  return this;
};

jQuery.fn.setFormTimeout = function() {
  var $form = $(this);
  setTimeout(function() {
    $('input[type="submit"]', $form).button('reset');
    alert('Form failed to submit within 30 seconds');
  }, 30000);
};

2

Sử dụng hai nút gửi.

<input id="sub" name="sub" type="submit" value="OK, Save">
<input id="sub2" name="sub2" type="submit" value="Hidden Submit" style="display:none">

Và jQuery:

$("#sub").click(function(){
  $(this).val("Please wait..");
  $(this).attr("disabled","disabled");
  $("#sub2").click();
});

2

Sử dụng truy cập đơn giản trên trình.

    var submitCounter = 0;
    function monitor() {
        submitCounter++;
        if (submitCounter < 2) {
            console.log('Submitted. Attempt: ' + submitCounter);
            return true;
        }
        console.log('Not Submitted. Attempt: ' + submitCounter);
        return false;
    }

Và gọi monitor()chức năng trên trình mẫu.

    <form action="/someAction.go" onsubmit="return monitor();" method="POST">
        ....
        <input type="submit" value="Save Data">
    </form>

1

Tôi đã gặp vấn đề tương tự và (các) giải pháp của tôi như sau.

Nếu bạn không có bất kỳ xác nhận hợp lệ nào của máy khách thì bạn chỉ cần sử dụng phương thức jquery one () như được ghi lại ở đây.

http://api.jquery.com/one/

Điều này vô hiệu hóa xử lý sau khi nó được gọi.

$("#mysavebuttonid").on("click", function () {
  $('form').submit();
});

Nếu bạn đang thực hiện xác nhận phía máy khách như tôi đang làm thì nó hơi phức tạp hơn một chút. Ví dụ trên sẽ không cho phép bạn gửi lại sau khi xác thực thất bại. Thay vào đó hãy thử phương pháp này

$("#mysavebuttonid").on("click", function (event) {
  $('form').submit();
  if (boolFormPassedClientSideValidation) {
        //form has passed client side validation and is going to be saved
        //now disable this button from future presses
        $(this).off(event);
   }
});

1

Bạn có thể dừng lần gửi thứ hai bằng cách này

$("form").submit(function() {
        // submit more than once return false
        $(this).submit(function() {
            return false;
        });
        // submit once return true
        return true; // or do what you want to do
    });
});

0

Giải pháp của tôi:

// jQuery plugin to prevent double submission of forms
$.fn.preventDoubleSubmission = function () {
    var $form = $(this);

    $form.find('[type="submit"]').click(function () {
        $(this).prop('disabled', true);
        $form.submit();
    });

    // Keep chainability
    return this;
};

0

Trong trường hợp của tôi, onsubmit của biểu mẫu có một số mã xác thực, vì vậy tôi tăng câu trả lời của Nathan Long bao gồm một điểm kiểm tra onsubmit

$.fn.preventDoubleSubmission = function() {
      $(this).on('submit',function(e){
        var $form = $(this);
        //if the form has something in onsubmit
        var submitCode = $form.attr('onsubmit');
        if(submitCode != undefined && submitCode != ''){
            var submitFunction = new Function (submitCode);
            if(!submitFunction()){
                event.preventDefault();
                return false;
            }                   
        }

        if ($form.data('submitted') === true) {
            /*Previously submitted - don't submit again */
            e.preventDefault();
        } else {
          /*Mark it so that the next submit can be ignored*/
          $form.data('submitted', true);
        }
      });

      /*Keep chainability*/
      return this;
    };

0

Thay đổi nút gửi:

<input id="submitButtonId" type="submit" value="Delete" />

Với nút bình thường:

<input id="submitButtonId" type="button" value="Delete" />

Sau đó sử dụng chức năng nhấp:

$("#submitButtonId").click(function () {
        $('#submitButtonId').prop('disabled', true);
        $('#myForm').submit();
    });

Và nhớ nút kích hoạt lại khi cần thiết:

$('#submitButtonId').prop('disabled', false);

0

Tôi không thể tin được mẹo css lỗi thời của các sự kiện con trỏ: chưa có gì được đề cập. Tôi đã có vấn đề tương tự bằng cách thêm một thuộc tính bị vô hiệu hóa nhưng điều này không đăng lại. Hãy thử bên dưới và thay thế #SubmitButton bằng ID của nút gửi của bạn.

$(document).on('click', '#SubmitButton', function () {
    $(this).css('pointer-events', 'none');
})

Điều này không dừng gửi biểu mẫu khi người dùng nhấn Enterphím.
cải cách

0

Tại sao không chỉ thế này - điều này sẽ gửi biểu mẫu mà còn vô hiệu hóa nút gửi,

   $('#myForm').on('submit', function(e) {
       var clickedSubmit = $(this).find('input[type=submit]:focus');
       $(clickedSubmit).prop('disabled', true);
   });

Ngoài ra, nếu bạn đang sử dụng Xác thực jQuery, bạn có thể đặt hai dòng này bên dưới if ($('#myForm').valid()).


0

mã này sẽ hiển thị tải trên nhãn nút và đặt nút thành

vô hiệu hóa trạng thái, sau đó sau khi xử lý, bật lại và quay lại văn bản nút gốc **

$(function () {

    $(".btn-Loading").each(function (idx, elm) {
        $(elm).click(function () {
            //do processing
            if ($(".input-validation-error").length > 0)
                return;
            $(this).attr("label", $(this).text()).text("loading ....");
            $(this).delay(1000).animate({ disabled: true }, 1000, function () {
                //original event call
                $.when($(elm).delay(1000).one("click")).done(function () {
                    $(this).animate({ disabled: false }, 1000, function () {
                        $(this).text($(this).attr("label"));
                    })
                });
                //processing finalized
            });
        });
    });
    // and fire it after definition
});

-1

Tôi đã giải quyết một vấn đề rất giống nhau bằng cách sử dụng:

$("#my_form").submit(function(){
    $('input[type=submit]').click(function(event){
        event.preventDefault();
    });
});
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.