代码之家  ›  专栏  ›  技术社区  ›  Hazel へいぜる

当单击未在内容外部启动时阻止模式关闭?

  •  0
  • Hazel へいぜる  · 技术社区  · 5 年前

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

    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 而不是点击?
      • 成功了!我想今天早上需要更多的咖啡。今天晚些时候会给未来的读者写一个完整的答案。
    0 回复  |  直到 5 年前
        1
  •  2
  •   Roko C. Buljan    5 年前

    在你用合理的理由回答自己之前(如你的问题编辑中所述)- 考虑到:

    • onmousedown 可能并不总是理想的用户体验。(有时有经验的用户撤销未注册为 click 他们故意将鼠标移到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>
        2
  •  1
  •   Evgeny Melnikov    5 年前

    这是一个有效的例子。想想,它符合你的需要)

    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>
        3
  •  1
  •   Hazel へいぜる    5 年前

    为了自己回答这个问题,我想 onclick 事件正在工作。单击定义为按下鼠标按钮,然后释放鼠标按钮。这两点都必须发生才能导致 onclick公司 要引发的事件(尽管在某个时间点之前或之后不能没有另一个事件发生)。

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

    1. 用户按下鼠标按钮。

    2. 这个 onmousedown 引发事件。

    3. 用户释放鼠标按钮。

    4. 这个 onmouseup 引发事件。

    5. 这个 onmouseclick 引发事件。

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

    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公司 事件。我认为有三种可能性(从最有可能到最不可能),当这是设置,没有一个我可以确认或否认:

    • 这个 目标 在释放鼠标按钮时设置。
    • 这个 目标 在按下鼠标按钮时设置,然后在释放鼠标按钮时再次设置。
    • 这个 目标 连续设置。

    在分析了我的想法之后,我决定在我的场景中 下榻 可能是最好的解决方案。这将确保只有当用户在内容区域外发起单击时,模式才会关闭。一个很好的结合方式 鼠标放开 为了确保仍然实现完全单击,下面将演示。但在我的情况下,我可以简单地使用 下榻 以下内容:

    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>

    上面的代码片段确保在关闭模式容器之前,单击在模式容器上开始和结束。如果用户意外地在内容区域外部启动单击并将鼠标拖到内容区域以完成单击,则此操作将阻止关闭模式。相反的顺序也是如此,只有在模态容器上启动并完成单击时,模态才会关闭。


    我唯一搞不清楚的是 目标 对于 onclick公司 对这个问题的根本原因的正确解释可能更重要,所以请随时告诉我!