在 Safari 中提交表单之前更改元素可见性有时不起作用

Posted

技术标签:

【中文标题】在 Safari 中提交表单之前更改元素可见性有时不起作用【英文标题】:Changing element visibility just before form submitting in Safari doesn't work sometimes 【发布时间】:2021-08-07 01:26:56 【问题描述】:

这是我用来在整个应用程序中显示加载微调器的 html 元素(处于其原始状态)(主要使用 Bootstrap 类):

<div id="loadSpinner" class="overlay d-flex justify-content-center invisible">
  ...
</div>

这是视图中的表单(Rails 6):

  <%= form_with(url: result_path, 
      method: "patch", local: true) do |f| %>

    ...

    <%= f.submit "Finish", class: "btn btn-success btn-lg my-3",
        onclick: "finish();" %>

  <% end %>

这是显示加载微调器的“finish()”js 函数:

function finish() 
  ...
  document.querySelector('#loadSpinner').classList.remove('invisible');

它在 Chrome 和 Firefox 中运行良好,但加载微调器有时不显示在 Safari 中。

我已经添加了一些日志(console.log(document.querySelector('#loadSpinner')) 在 'invisible' 类删除之后)并且可以看到 'invisible' 类在提交之前实际上被删除了。所以 js 代码工作正常,但微调器没有出现。

另外,我尝试在删除“不可见”类后添加一个“可见”类 - 问题仍然存在。

有人知道为什么会在 Safari 中发生吗?

我有一个假设,实际上提交是在实际删除“不可见”类之前以某种方式发生的,但我不知道检查和修复它的方法。

【问题讨论】:

【参考方案1】:

如果您使用的是 Rails UJS,您可以使用 ajax:beforeSendajax:complete 事件来切换微调器:

<%= form_with(url: result_path, method: "patch", remote: true, class: 'spinner-form') do |f| %>
let form = document.querySelector('#myForm');
let spinner = document.querySelector('#loadSpinner');

document.addEventListener('ajax:beforeSend', (event)=>
  if (!event.target.matches('.spinner-form)) return;
  spinner.classList.remove('invisible');
);

document.addEventListener('ajax:complete', (event)=>
  if (!event.target.matches('.spinner-form)) return;
  spinner.classList.add('invisible');
);

如果你自己做,你想阻止默认事件处理程序:

<%= f.submit "Finish", class: "btn btn-success btn-lg my-3",
        id: "myButton" %>
let btn = document.querySelector('#myButton');
let spinner = document.querySelector('#loadSpinner');

btn.addEventListener('click', (event)=>
  let form = event.target.form;
  event.preventDefault();
  spinner.classList.add('invisible');

  fetch(form.action)
    .then((response)=>
       spinner.classList.remove('invisible');
     );
);

如果这不是 XHR (ajax) 请求,那么当页面重新加载时脚本执行会停止,这将无法正常工作。

【讨论】:

这不是 ajax。我必须将其保留在“本地:真实”中。无论如何,这与 Safari 有什么关系? 不是很多。我怀疑你所做的实际上在任何浏览器中都能可靠地工作,因为它在概念上被破坏了。 为什么?我认为 onclick js-functions 必须在实际提交之前执行。而且我在其他浏览器中看不到这个问题。 除非您的函数实际上是阻塞的,例如调用alert 或者您停止事件默认情况下表单仍将提交,这实际上只是一个时间问题。您可能还没有发现它在其他浏览器上已损坏... 是的。使用真正的事件处理程序而不是 onclick。使用event.preventDefault() 阻止默认设置 - 做你的事,然后使用event.target.form.submit() 触发表单提交。【参考方案2】:

由于某种原因,Safari 在提交表单之前需要稍许延迟。

所以解决方法如下。

    将表单中的提交按钮改为简单按钮:
<%= form_with(url: result_path, 
    method: "patch", local: true) do |f| %>

...
    
<%= button_tag "Finish", type: "button",
          class: "btn btn-success btn-lg my-3",
          onclick: "finish();" %>

<% end %>
    稍微延迟提交数据:
function finish() 
  ...
  document.querySelector('#loadSpinner').classList.remove('invisible');
  
  // submit form
  setTimeout(function() 
    document.querySelector('form').submit();
  , 300);  

【讨论】:

以上是关于在 Safari 中提交表单之前更改元素可见性有时不起作用的主要内容,如果未能解决你的问题,请参考以下文章

移动 Safari 独立模式中的可见性更改

WICKET:控制组件在更改 CheckBox 时的可见性

小部件可见性更改时在 MacOS 中呈现 Qt 错误表单

Vuejs - 如何在单个表单中仅提交可见元素(使用 Vuelidate)

使用 Mobile Safari“表单助手”在选择元素上未触发更改事件

如果在 jQuery 中不显示,则更改另一个元素的可见性