代码之家  ›  专栏  ›  技术社区  ›  Max Williams

禁用窗体和所有包含的元素,直到Ajax查询完成(或另一个防止多个远程请求的解决方案)

  •  4
  • Max Williams  · 技术社区  · 15 年前

    我有一个包含输入和选择的搜索表单,当任何输入/选择被更改时,我运行一些JS,然后用jquery进行Ajax查询。我想阻止用户在请求进行时对表单进行进一步的更改,因为此时他们可以一次启动多个远程搜索,从而有效地导致不同搜索之间的竞争。

    似乎最好的解决方案是防止用户在等待请求返回时与表单交互。目前,我正在以最愚蠢的方式进行这项工作,在进行Ajax查询之前隐藏表单,然后在成功/错误时再次显示它。这解决了问题,但看起来很糟糕,并不能真正接受。还有其他更好的方法来防止与表单交互吗?为了使事情变得更复杂,允许漂亮的选择,用户实际上与连接了JS的跨度进行交互,以将它们与实际隐藏的选择相关联。因此,即使跨度不是输入,它们也包含在表单中并表示表单的实际交互元素。

    感谢任何建议-麦克斯。我现在做的是:

    function submitQuestionSearchForm(){
      //bunch of irrelevant stuff  
      var questionSearchForm = jQuery("#searchForm");
      questionSearchForm.addClass("searching");
      jQuery.ajax({
        async: true, 
        data: jQuery.param(questionSearchForm.serializeArray()), 
        dataType: 'script', 
        type: 'get', 
        url: "/questions", 
        success: function(msg){ 
          //more irrelevant stuff
          questionSearchForm.removeClass("searching");                           
        },
        error: function(msg){ 
          questionSearchForm.removeClass("searching");                           
        }    
      }); 
      return true;
    }
    

    “搜索”类当前只有显示:无

    5 回复  |  直到 8 年前
        1
  •  5
  •   JOBG    8 年前

    我用这个插件 http://jquery.malsup.com/block/ 它实现了一个半透明的img,可以应用于DIV或整个页面,非常好,支持跨浏览器。

    编辑: 正如一些人所说,这个插件不会阻塞表单输入、选择等。如果你真的希望它是防弹的,并且不允许人们乱摆弄你需要实现进一步阻塞的表单,我建议使用纯HTML解决方案,类似于这样 https://stackoverflow.com/a/36586206/118447

        2
  •  1
  •   Sjeiti    8 年前

    所有已发布的解决方案都不能解释键盘交互。

    您可以简单地添加 disabled 属性设置为提交时的提交按钮(并在服务器响应时将其删除)。但你仍然可以通过textfield提交焦点键盘返回。 (添加) 残疾人 不过还是不错的用户体验)

    防止用户与表单交互的一个更强大的解决方案是防止事件到达元素。

    快速而肮脏的测试表明你只需要 preventDefault() 事件 keydown , click , dragstart drop . 另外,对于手机,我们需要 blur() 任何聚焦的输入元素(仅测试Android)。

    下面是一个简单的例子(后两种方法是其要点):

    const form = document.querySelector('form')
    const status = document.querySelector('.status')
    const showStatus = s=>status.textContent=s||''
    const events = ['keydown','click','dragstart','drop','focus']
    
    form.addEventListener('submit',e=>{
        e.preventDefault()
        lockForm(form,true)
        showStatus('submitting')
        setTimeout(fakeResponse,10000)
    })
    
    function fakeResponse(){
        lockForm(form,false)
        showStatus()
    }
    
    function lockForm(form,lock){
        lock
            ?events.forEach(event=>form.addEventListener(event,preventDefault,false))
            :events.forEach(event=>form.removeEventListener(event,preventDefault,false))
    }
    
    function preventDefault(e){
        e.target.blur()
        e.preventDefault()
    }
    body {
        font-family: Arial, sans;
        line-height: 100%;
    }
    label {
        display: block;
        margin-bottom: 10px;
        box-shadow: 0 -1px 0 0 #EEE inset;
    }
    label:after {
        content: '';
        display: table;
        clear: both;
    }
    input, textarea {
        width: 50%;
        float: right;
    }
    .status {
        margin-top: 20px;
        line-height: 200%;
        text-align: center;
        background-color: #FF0;
    }
    <form method="GET" action="https://httpbin.org/get">
        <h3>form disables while submitting by stopping events</h3>
    
        <label>text<input></label>
        <label>text disabled<input disabled></label>
    
        <label>textarea<textarea></textarea></label>
        <label>textarea disabled<textarea disabled></textarea></label>
    
        <label>checkbox<input type="checkbox"></label>
        <label>checkbox disabled<input type="checkbox" disabled></label>
    
        <button type="submit">submit</button>
    </form>
    <div class="status"></div>
    <h3>elements outside the form still function normally</h3>
    <label>text<input></label>
        3
  •  0
  •   Adirael    15 年前

    当Ajax请求启动时,可以显示一个DIV来获取所有的页面空间(可以将加载背景放在中间并使其透明),并在完成后隐藏它。

    我认为您也可以使调用同步而非异步,我认为它将“阻塞”导航器,直到请求完成。

        4
  •  0
  •   Intelekshual    15 年前

    如何使用一个布尔值来防止多个Ajax请求被触发?还可以使用布尔值禁用/启用元素。

    类似于以下内容(有关我的添加,请参阅注释):

    // set initial boolean state
    var isRequestFinished = true;
    
    function submitQuestionSearchForm(){
    
        // check if previous request has finished
        if(isRequestFinished){
            // request just started, so set boolean to false
            isRequestFinished = false;
    
            jQuery.ajax({
                async: true, 
                data: jQuery.param(questionSearchForm.serializeArray()), 
                dataType: 'script', 
                type: 'get', 
                url: "/questions", 
                success: function(msg){ 
                  questionSearchForm.removeClass("searching");
                  // request just finished, so set boolean to true
                  isRequestFinished = true;
                },
                error: function(msg){ 
                  questionSearchForm.removeClass("searching");                           
                }    
            });
        }
        return true;
    };
    

    我没有测试这段代码,但它应该为您指明正确的方向。在你打电话之前 jQuery.ajax() 您可以禁用所需的任何元素,然后在成功回调中重新启用它们。

        5
  •  0
  •   Max Williams    12 年前

    实际上,我以一种与我想象的不同的方式解决了这个问题:我没有阻止用户与表单交互,而是启动一个新的Ajax请求,并中止以前的请求。因此,服务器仍然受到多个搜索的攻击,但是除了最近的搜索,我忽略了所有搜索的响应。

    以下是我如何设置表单以实现此目的(我添加了大量空白以使其更清晰):

    <form 
      onsubmit="if(typeof(remoteSearch) != "undefined"){ 
                  remoteSearch.abort();
                }
                remoteSearch = jQuery.ajax({
                                 async:true, 
                                 data:jQuery.param(jQuery(this).serializeArray()), 
                                 dataType:'script', 
                                 type:'get', 
                                 url:'/my-search-url'
                               }); 
                return false;" 
      method="get" 
      action="/my-search-url"
    >  
    

    这里没有成功块,因为我有一个单独的处理程序来处理Ajax调用的结果,但是您可以添加一个。