【问题标题】:How to freeze browser window intentionally (like alert, confirm and prompt does)?如何故意冻结浏览器窗口(如警报、确认和提示)?
【发布时间】:2021-03-20 02:58:53
【问题描述】:

有没有办法像alertconfirmprompt(简称:“acp”)那样故意冻结浏览器窗口?

我想显示一个带有自定义 css 的模态对话框,而不是“acp”的本机弹出窗口,但也希望能够冻结浏览器,直到我有用户反馈,就像“acp”一样。

但是,伙计,为什么?这是不好的做法(呃,我必须投反对票)!
因此,如果这是不好的做法 - 那么为什么“acp”实际上提供了这种同步行为? 因为在某些特定场景中,它恰好是适合 UX 的正确工具。那些原生模式看起来确实很丑,同时也非常有限。

这只是一个快速而肮脏的例子,在用户给出反馈之前冻结浏览器是完全可以的。假设我们有一个 form 和一个 input[type="reset"] 元素。因此,在我们真正让form 重置之前,我们会询问用户类似的问题:“您确定要重置(数据会丢失)吗?”。

如果我对本机模态外观(我不是)感到满意,我可以这样做:

var handleSubmit = function( e ) {
  confirm('Are you sure you want to reset (data will be lost)?') || e.preventDefault()
};
$('form').on( 'reset', handleSubmit ); // just using jQuery here for the sake of shortness
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<form>
  <input placeholder="type something, reset and cancel to keep input value" style="width:100%">
  <input type="reset">
</form>

所以“每个人”都应该同意(编辑:或者至少有人可以)这不是坏习惯,对吧?
但是我们如何使用自定义样式的模态/对话框/弹出窗口来实现相同的效果呢?
我 - 当然 - 不是要求 HTML/CSS 部分,而是要求通过 JavaScript 冻结浏览器窗口的能力!

老实说,我实际上希望有人对此投反对票,但也许有一个 Killer JavaScript Ninja,他有这个特殊的 hack 使其成为可能...


注意:我确切地知道如何最终获得相同的结果,但我正在寻找一个实际的冻结/同步功能,以保持代码直截了当,减少 hack-ish。

【问题讨论】:

  • 所以“每个人”都应该同意这不是坏习惯,对吧?不,不,我不知道。仅仅因为旧的本机方法冻结了浏览器,并不意味着您的逻辑应该如此。阻止用户与页面交互并不意味着您必须冻结浏览器。覆盖的整个概念就是为了这个目的而存在的。
  • 你不能,但你可以让它看起来像你有。例如,如果您查看 Bootstrap 模态。 getbootstrap.com/docs/4.0/components/modal 在您的示例中,不使用重置按钮,而是显示您的模式,然后手动调用表单上的重置。
  • “那么,如果这是不好的做法 - 那么为什么“acp”实际上提供了这种同步行为?因为在某些特定场景中,它恰好是适合 UX 的正确工具。” i> 我从来没有见过一个 UXer 支持冻结浏览器和删除用户控制作为一种积极的体验。
  • 不确定你想要什么答案,。这是不可能的。主要原因是如果你冻结浏览器,你认为什么会渲染 UI,你刚刚阻塞了浏览器的事件循环,它将无法处理来自用户的任何输入。同步方法确实感觉不错,但如果你使用async / await,你可以使用const sure = await showConfirm();,它所需要的只是你的确认对话框成为一个承诺。
  • 是的,这就是我说你手动调用它的原因。反正我就是这样做的,似乎并没有让代码变得更复杂,特别是如果你的人引用quite experinced..

标签: javascript browser window synchronous


【解决方案1】:

您不能像原生对话框那样冻结浏览器应用程序。这些不是用 JavaScript 构建的,它们是用原生代码构建的,可以以任何方式影响浏览器应用程序。禁止 JavaScript 以这种方式影响客户端应用程序。

但是,您可以在浏览器窗口中冻结与页面内容的交互......

只需在页面上方放置一个大小为div 且位置固定的窗口。这将阻止用户与主页(后面)上的任何内容进行交互。然后,在上面显示您的模式对话框。当用户清除模态框时,将其和窗口大小的 div 隐藏起来,从而使主页面再次交互。

let modal = document.querySelector(".modal");
let pageCover = document.querySelector(".pageCover");
let main = document.querySelector("main");        

document.getElementById("open").addEventListener("click", function(){
  modal.classList.remove("hidden");
  pageCover.classList.remove("hidden"); 
  main.addEventListener("focus", preventFocus);
});


document.getElementById("close").addEventListener("click", function(){
  modal.classList.add("hidden");
  pageCover.classList.add("hidden"); 
  main.removeEventListener("focus", preventFocus);
});

function preventFocus (evt){
  evt.preventDefault();
}
.hidden { display:none; }

.pageCover { 
  position:fixed; 
  z-index:10; 
  background-color:rgba(0,0,0,.25); 
  width:100vw; 
  height:100vh; 
  top:0; 
  left:0;
}

.modal { 
  position:absolute; 
  z-index:100; 
  background-color:aliceblue;
  border:2px double grey;
  width:200px; 
  height:200px; 
  top:30%; 
  left:30%; 
  text-align:center;
}

.modalTitle {
  margin:0;
  background-color:#00a;
  color:#ff0;
  padding:5px;
  font-weight:bold;
  font-size:1.1em;
}

#close { 
  background-color:#800080;
  color:#ff0;
  border:1px solid #e0e0e0;
  padding:5px 10px;
}

#close:hover { background-color:#c000c0; }
<main>
  <h1>This is some page content</h1>
  <p>And more content</p>
  <button id="open">Click Me To Show Modal</button>
  <div>More content</div>
</main>

<div class="hidden pageCover"></div>

<div class="hidden modal">
  <div class="modalTitle">Modal Title Here</div>
  <p>
    Now, you can only interact with the contents of this "modal".
  </p>
  <button id="close" >Close</button>
</div>

【讨论】:

  • @Taplar 是的,但是如果你tabindex 放在元素上,那么你不能。
  • @Axel 没有什么可留下的。我回答的第一段是回答你的问题。第二部分只是一个模拟的建议。如果不适合您,请忽略它。
  • @Axel 哦,那是什么问题,使用确认。出于某种原因,我认为您想冻结浏览器并创建一个自定义外观对话框。这样做当然会很麻烦..
  • @Axel 我不确定你想让大家说什么,你到底想要什么?,你想要一个花哨的对话框来确认表单的重置。如果你这样做,这是可能的,SO 上的人会很乐意提供帮助。如果您希望 W3C 阅读您的帖子,然后说,好吧,我们似乎是时候在 SO 上实现浏览器冻结功能 Axel 了,我不确定它是否会发生。
  • @Axel 我的答案的第一段说什么?你已经得到了几个人的答案。你似乎只是不喜欢它,但继续争论并不会改变它。
【解决方案2】:

除了弹出窗口之外,您可以将所有内容放在 div 中。然后您可以通过 css 打开/关闭指针事件。

div 中的所有内容都将不再是可交互的。这将给出冻结浏览器窗口的外观。

.freeze { pointer-events: none; }

注意:pointer-events 添加到元素时,这也会影响所有子元素,因此将其放在主体上也会锁定弹出窗口。

关闭弹出窗口后,我们可以从 div 中删除 freeze 类,一切都会重新开始工作。

const content = document.getElementById('content')
const overlay = document.querySelector('.overlay')
const a = document.querySelector('.open-overlay')
const close = document.querySelector('.overlay .close')

// Open a popup and freeze the browser content
a.addEventListener('click', e => {
  e.preventDefault()
  content.classList.add('freeze')
  overlay.classList.remove('hidden')
})

// Close the popup window and unfreeze the browser content
close.addEventListener('click', e => {
  e.preventDefault()
  content.classList.remove('freeze')
  overlay.classList.add('hidden')
})
.freeze { pointer-events: none; }

.hidden { display: none; }

.overlay {
  position: fixed;
  z-index: 100;
  padding: 20px;
  background: white;
  border: solid 1px #ccc;
  width: 300px;
  top: 20px;
  left: 0;
  right: 0;
  margin: auto;
}
<div class="overlay hidden">
  <h1>Overlay</h1>
  <a href="" class="close">Close</a>
</div>

<div id="content">
  <a href="" class="open-overlay">Open Overlay</a>

  <p>Hello World</p>
  <p>Hello World</p>
  <p>Hello World</p>
  <p>Hello World</p>
  <p>Hello World</p>
  <form>
    <input type="text">
  </form>
</div>

【讨论】:

  • 但我仍然可以通过 Tab 键在后台导航。我什至仍然可以在input 中输入另一个值。就像我在我的问题中所写的那样:它不是关于 HTML/CSS,而是关于冻结方面......
  • 就像其他人所说的,你不能修改物理浏览器的行为,你只能模拟它。
  • 那么这实际上是正确的答案!有什么可以从规格中支持这一点???
  • 您在寻找什么样的规格? Pointer Events?
  • 没有规范。
猜你喜欢
  • 2010-11-17
  • 1970-01-01
  • 2018-06-27
  • 2023-02-05
  • 2017-06-30
  • 1970-01-01
  • 1970-01-01
  • 2014-03-27
相关资源
最近更新 更多