当点击不是从内容外部发起时,阻止模式关闭?

时间:2019-06-17 14:39:53

标签: javascript

我创建了一个简单的模式,当您在内容区域之外单击时,可以将其关闭。这是设计使然,但具有意外的副作用。如果我单击内容区域中的任何位置(例如,在文本字段中)并将鼠标拖动到内容区域之外,然后释放单击,它将关闭模式。我经常有这样做的习惯,并且可以看到普通用户将其视为错误的原因,因此我试图在发布前先加以解决。

var modal = document.getElementById("modal-container");
function openModal() { modal.classList.add("active"); }
function closeModal() { modal.classList.remove("active"); }
window.onclick = function (event) {
    if (event.target == modal)
        closeModal();
}
html, body {
  margin: 0;
  height: 100%;
}
.modal-container.active { top: 0; }
.modal-container {
  position: absolute;
  top: -500vh;
  left: 0;
  width: 100%;
  height: 100%;
  display: grid;
  background-color: rgba(0, 0, 0, 0.75);
}

.modal-content {
  height: 50%;
  width: 50%;
  margin: auto;
  background-color: #fff;
  display: flex;
  align-items: center;
  justify-content: center;
}
<button onclick="openModal();">Open the Modal</button>
<div id="modal-container" class="modal-container">
  <div class="modal-content">
    <input type="text" />
  </div>
</div>

要正确测试:

  1. 点击“打开模态”按钮。
  2. 单击白板中央的文本框。
  3. 输入一些文字。
  4. 在文本框中按下鼠标左键。
  5. 将鼠标拖动到白色面板的边界之外。
  6. 释放鼠标按钮。

现在应该关闭模式。


有没有一种方法可以在不跟踪鼠标坐标的情况下防止这种情况发生?

  • 也许是onmousedown而不是点击?
    • 行得通!我想今天早上需要多喝咖啡。今天晚些时候要为未来的读者写一个详尽的答案。

3 个答案:

答案 0 :(得分:1)

在以有效原因回答自己之前(如问题编辑中所述)- 考虑:

  • onmousedown可能并不总是所需的UX。 (有时,有经验的用户可以撤消未注册为click的mousedown,他们故意将鼠标移到mouseup事件的另一个元素上,只是保留当前状态。)
  • 删除嵌入式JavaScript
  • 使用Element.addEventListener()将侦听器分配给具有data-modal属性的任何按钮
  • 甚至不使用容器元素使用data-modal="#some_modal_id"
  • 最后:使用if (evt.target !== this) return;

const el_dataModal = document.querySelectorAll('[data-modal]');

function toggleModal(evt) {
  if (evt.target !== this) return; // Do nothing if the element that propagated the event is not the `this` button which has the event attached.
  const id = evt.currentTarget.getAttribute('data-modal');
  document.querySelector(id).classList.toggle('active');
}

el_dataModal.forEach(el => el.addEventListener('click', toggleModal));
html, body {
  margin: 0;
  height: 100%;
}
.modal-container {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  display: grid;
  background-color: rgba(0, 0, 0, 0.75);
  opacity: 0; /* ADDED */
  transition: 0.26s; /* ADDED */
  visibility: hidden; /* ADDED */
}
.modal-container.active {
  opacity: 1; /* ADDED */
  visibility: visible; /* ADDED */
}
.modal-content {
  height: 50%;
  width: 50%;
  margin: auto;
  background-color: #fff;
  display: flex;
  align-items: center;
  justify-content: center;
}
<button data-modal="#modal-container">Open the Modal</button>

<div id="modal-container" class="modal-container" data-modal="#modal-container">
  <div class="modal-content">
    <input type="text">
    <br><br>
    <button data-modal="#modal-container">CLOSE MODAL TEST</button>
  </div>
</div>

答案 1 :(得分:1)

这是工作示例。想一想,它符合您的需求))

var clickTarget = null;
var modal = document.getElementById("modal-container");

function openModal() {
    modal.classList.add("active");
    document.body.addEventListener('mousedown', onModalMouseDown, false);
    document.body.addEventListener('mouseup', onModalMouseUp, false);
}

function closeModal() {
    modal.classList.remove("active");
    document.body.removeEventListener('mousedown', onModalMouseDown);
    document.body.removeEventListener('mouseup', onModalMouseUp);
}

function onModalMouseDown(event) {
    clickTarget = event.target;
}

function onModalMouseUp() {
    if (clickTarget === modal) {
        closeModal();
    }
}
html, body {
  margin: 0;
  height: 100%;
}
.modal-container.active { top: 0; }
.modal-container {
  position: absolute;
  top: -500vh;
  left: 0;
  width: 100%;
  height: 100%;
  display: grid;
  background-color: rgba(0, 0, 0, 0.75);
}

.modal-content {
  height: 50%;
  width: 50%;
  margin: auto;
  background-color: #fff;
  display: flex;
  align-items: center;
  justify-content: center;
}

.modal-trigger-btn {
  margin: 20px;
  font-size: 16px;
}
<button onmousedown="openModal();" class="modal-trigger-btn">Open the Modal</button>
<div id="modal-container" class="modal-container">
  <div class="modal-content">
    <input type="text" placeholder="Start to drag outside..."/>
  </div>
</div>

答案 2 :(得分:1)

要亲自回答这个问题,我考虑了onclick事件的工作方式。单击的定义是按下并释放鼠标按钮。必须同时发生这两个点才能引发onclick事件(尽管在之前或之后的某个时刻没有其他事件发生,您实际上无法拥有一个事件。)

我没有在下面的执行路径上找到任何真实的文档,因此它是基于逻辑推论的。 如果您对此有任何文档,请在评论中链接它,以便我进行审阅并为以后的读者调整答案。

  
      
  1. 用户按下鼠标按钮。

  2.   
  3. 引发onmousedown事件。

  4.   
  5. 用户释放鼠标按钮。

  6.   
  7. 引发onmouseup事件。

  8.   
  9. 引发onmouseclick事件。

  10.   

我确实编写了一个测试来验证这些结果:

var ePath = document.getElementById("executionPath");
document.body.onmousedown = function (event) { ePath.innerHTML += "On Mouse Down<br>"; }
document.body.onmouseup = function (event) { ePath.innerHTML += "On Mouse Up<br>"; }
document.body.onclick = function (event) { ePath.innerHTML += "On Click<br>"; }
html, body { height: 100%; }
<p id="executionPath">Click the Window<br></p>

我认为,意外行为是由于为target事件设置了onclick而引起的。我认为设置此设置时存在三种可能性(从最大到最小),我无法确认或否认:

  • 释放鼠标按钮时设置target
  • target在按下鼠标按钮时设置,然后在释放鼠标按钮时再次设置。
  • target是连续设置的。

经过分析,我确定对于我的情况onmousedown可能是最佳解决方案。这将确保仅当用户在内容区域之外启动单击时,模式才会关闭。下面展示了一种与onmouseup结合使用以确保仍然实现完整点击的好方法。尽管就我而言,我可以简单地使用onmousedown

var initialTarget = null;
var modal = document.getElementById("modal-container");
function openModal() { modal.classList.add("active"); }
function closeModal() { modal.classList.remove("active"); }
window.onmousedown = function (event) { initialTarget = event.target; }
window.onmouseup = function (event) {
    if (event.target == initialTarget)
        closeModal();
}
html, body { height: 100%; }
.modal-container.active { top: 0; }
.modal-container {
  position: absolute;
  top: -500vh;
  left: 0;
  width: 100%;
  height: 100%;
  display: grid;
  background-color: rgba(0, 0, 0, 0.75);
}

.modal-content {
  height: 50%;
  width: 50%;
  margin: auto;
  background-color: #fff;
  display: flex;
  align-items: center;
  justify-content: center;
}
<button onclick="openModal();">Open the Modal</button>
<div id="modal-container" class="modal-container">
  <div class="modal-content">
    <input type="text" />
  </div>
</div>

上面的代码段可确保在关闭模式之前先在模式容器上单击并结束。如果用户不小心在内容区域之外发起了单击并将鼠标拖到内容区域以完成单击,则这可以防止模式关闭。反之亦然,只有在模式容器上启动并完成点击后,模式才会关闭。


我唯一不知道的是设置了target的{​​{1}}时,这可能在正确解释此问题的根本原因上更为重要,请随时让我知道!