html 阻止除滚动外的所有鼠标事件

8wigbo56  于 2023-10-14  发布在  其他
关注(0)|答案(7)|浏览(103)

现在我正在使用某种类型的 * 加载组件 *(实际上是一个具有强大透明度和加载微调器的绝对定位div)来禁用某些输入/区域并显示它们正在加载。
但是如果它位于可滚动元素(div)之上,我仍然希望能够滚动它(使用鼠标滚轮/触摸输入)。

<div style="display: flex;">
<div style="position: relative; display: flex">
  <div
    style="
      position: absolute;
      left: 0;
      right: 0;
      top: 0;
      bottom: 0;
      background-color: white;
      opacity: 0.5;
      pointer-events: none;
    "
  ></div>

  <div style="width: 200px; height: 200px; overflow: auto">
    <div style="height: 1000px">
      <button onclick="alert('YOU CANNOT TRIGGER ME!')">
        YOU CANNOT TRIGGER ME
      </button>
    </div>
  </div>
  <div style="width: 200px; height: 200px; overflow: auto">
    <div style="height: 1000px">
      <button onclick="alert('YOU CANNOT TRIGGER ME!')">
        YOU CANNOT TRIGGER ME, TOO!
      </button>
    </div>
  </div>
</div>

<div style="position: relative; display: flex">
  <div style="width: 200px; height: 200px; overflow: auto">
    <div style="height: 1000px">
      <button onclick="alert('Im still useable!')">
        I'm still useable
      </button>
    </div>
  </div>
</div>
</div>

使用pointer-events: none;当然适用于滚动事件,但不会阻止例如。按钮被按下。如何做到这一点?
在此解决方案中,内容被阻止但也不可滚动:

<div style="display: flex;">
<div style="position: relative; display: flex">
  <div
    style="
      position: absolute;
      left: 0;
      right: 0;
      top: 0;
      bottom: 0;
      background-color: white;
      opacity: 0.5;
    "
  ></div>

  <div style="width: 200px; height: 200px; overflow: auto">
    <div style="height: 1000px">
      <button onclick="alert('YOU CANNOT TRIGGER ME!')">
        YOU CANNOT TRIGGER ME
      </button>
    </div>
  </div>

  <div style="width: 200px; height: 200px; overflow: auto">
    <div style="height: 1000px">
      <button onclick="alert('YOU CANNOT TRIGGER ME!')">
        YOU CANNOT TRIGGER ME, TOO!
      </button>
    </div>
  </div>
</div>

<div style="position: relative; display: flex">
  <div style="width: 200px; height: 200px; overflow: auto">
    <div style="height: 1000px">
      <button onclick="alert('Im still useable!')">
        I'm still useable
      </button>
    </div>
  </div>
</div>
</div>

这是一个高度简化的最小再现。我希望半透明的div是一个通用的组件,可以放置在任何内容之上,而不需要知道/能够修改它。
编辑:
只是为了澄清,因为大多数解决方案/建议都试图调整褪色的内容:我想让 loaded-div 成为一个通用的控件,在用户仍然可以看到它的时候阻止它的底层内容被使用-一个经典的加载器。但是我仍然希望内容是可滚动的,这样用户仍然可以 * 检查 * 整个可见内容(同时不能与之交互)。这需要满足以下要求:

  • 我不知道里面的内容或可滚动的div数量
  • 我想尽可能少地调整内容(因为我不知道它会如何表现/删除我的处理程序等)。
  • 因此,对窗口进行一些处理(如已经建议的)可能是可能的?
vwkv1x7d

vwkv1x7d1#

我已经为你增加了更多的功能,现在检查一遍,如果没有让我知道

var scrollBlocker = document.getElementById('scrollBlocker');

        // Block all mouse events except scrolling
        window.addEventListener('mousedown', function (event) {
            // Check if it's not a scroll action or touch event
            if (event.button !== 1 && !event.touches) {
                event.preventDefault();
                scrollBlocker.style.pointerEvents = 'auto'; // Enable mouse events temporarily
            }
        });

        window.addEventListener('contextmenu', function (event) {
            event.preventDefault(); // Disable right-click context menu
        });

        // Re-enable mouse events on mouseup (when scrolling ends)
        window.addEventListener('mouseup', function () {
            scrollBlocker.style.pointerEvents = 'none'; // Block mouse events again
        });

        // Handle touch events
        window.addEventListener('touchstart', function (event) {
            // Disable touch events temporarily
            event.preventDefault();
            scrollBlocker.style.pointerEvents = 'auto';
        });

        window.addEventListener('touchend', function () {
            scrollBlocker.style.pointerEvents = 'none'; // Block touch events again
        });
<div style="position: relative">
        <div
            style="
                position: absolute;
                left: 0;
                right: 0;
                top: 0;
                bottom: 0;
                background-color: white;
                opacity: 0;
                pointer-events: auto;
            "
            id="scrollBlocker"
        ></div>
        <div style="width: 200px; height: 200px; overflow: auto">
            <div style="height: 1000px">
                <button onclick="alert('You cannot trigger me!');">
                    You cannot trigger me
                </button>
            </div>
        </div>
    </div>
unguejic

unguejic2#

向正在加载的元素添加一个loading类,然后通过赋予它们不透明性和禁用它们的指向事件来可视地禁用其中的输入。

#loader{
  position: absolute;
  display:none;
}
.loading label, .loading input, .loading button{
  opacity: 0.2;
  pointer-events: none;
}

脚本

$(document).ready(function(){
    $('.loader').show();
  let pos = $('#content').position();
  $('.loader').css('top', pos.top);
  $('.loader').css('left', pos.left);
  $('#content').addClass('loading');
});

让加载器本身和“动画”一样小,这样用户仍然可以与内容交互。
https://jsfiddle.net/zy0wup4a/2/
我自己也在我的一个应用程序中使用了这种方法,无限.content可以处于加载状态。

**Edit:**为了处理div本身的内联和附加的click处理程序,我提出了以下两个函数,在显示加载器和关闭加载器后调用:

$.fn.backupAndPreventOnClick = function(){
    return this.each(function(){
    let $el = $(this);
    if($el.prop('onclick')){
        $el.data('onClick', $el.prop('onclick'));
    }
    
    $el[0].onclick = function(event){
      event.preventDefault();
      event.stopImmediatePropagation();
      return false;
        };
  });
};

$.fn.resetOnClick = function(){
    return this.each(function(){
    let $el = $(this);
    $el.removeProp('onclick');
    if(!$el.data('onClick'))
        return;
      
        $el[0].onclick = $el.data('onClick');
    $el.removeData('onClick');
  });
};

看第二个小提琴:https://jsfiddle.net/amgcyLh1/
通过使用onclick属性本身,我们可以很容易地阻止其他侦听器。另一种解决方案是使用jQuery插件将函数注册为队列中的第一个。例如Attaching jQuery event handlers so that they are triggered first

ijxebb2r

ijxebb2r3#

为什么不循环所有控件并禁用除滚动外的所有事件?

document.addEventListener("mousedown", function(event) {
    //Code
    event.preventDefault();
});

document.addEventListener("wheel", function(event) {
    //Code
});

这是一个如何处理事件的例子,你可以使用for循环将它们添加到控件中。
我不擅长JS,但这是我如何处理我的Android和iOS应用程序的互联网断开。

bprjcwpo

bprjcwpo4#

第一个标记是在滚动元素的顶部使用透明覆盖,通过将覆盖的位置设置为绝对,将其不透明度设置为50%,
现在在修改后的标记中,通过使用top: unset并允许下面的内容可点击。然后通过设置pointer-events: none风格的按钮,使其不可点击。因此,为了使其再次可点击,包含了一个js函数,当触发滚动事件时,该函数将pointer-events: unset样式设置为按钮。

function addPointerEvents() {
  const sys3 = document.querySelector('.sys3');
  const btn = document.querySelector('button');

  sys3.addEventListener('scroll', handleScroll);
  sys3.addEventListener('touchscroll', handleScroll);

  function handleScroll() {
    btn.style.pointerEvents = 'unset';
  }
}

addPointerEvents();
<div style="position: relative">
  <div
    style="
      position: absolute;
      left: 0;
      right: 0;
      top: unset;
      bottom: 0;
      background-color: white;
      opacity: 0.5;
    "
  ></div>

  <div class="sys3" style="width: 200px; height: 200px; overflow: auto">
    <div style="height: 1000px">
      <button onclick="alert('YOU CANNOT TRIGGER ME!')" style="pointer-events: none;">
        YOU CANNOT TRIGGER ME
      </button>
    </div>
  </div>
</div>

我希望这是因为它是根据我的理解的OP的意图。

gzszwxb4

gzszwxb45#

也许是完全不同的方法。
将加载器完全放置在正在加载的内容的顶部,向其添加假的可滚动div,并将滚动传递给真实的内容div。
我不知道这种方法的缺点是什么。虽然现在还不是一个完整的答案,因为只处理垂直滚动:)

版本1

$(document).ready(function(){
    $("#loader").css('height', $('#content').height());
    $("#loader").css('width', $('#content').width());
    $("#loader-scroll").css('height', $('#content')[0].scrollHeight);

});

$('#loader').on('scroll', function(event){
    event.preventDefault();
  $('#content')[0].scrollTop = event.target.scrollTop;
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<div style="position: relative">
  <div id="loader"
    style="
      position: absolute;
      left: 0;
      right: 0;
      top: 0;
      bottom: 0;
      background-color: red;
      opacity: 0.5;
      z-index: 9999;
        overflow:auto;
      
    "
  ><div id="loader-scroll">
  </div></div>

  <div id="content" style="width: 200px; height: 200px; overflow: auto">
    <div style="height: 1000px">
      <button onclick="alert('YOU CANNOT TRIGGER ME!')">
        YOU CANNOT TRIGGER ME
      </button>
    </div>
  </div>
</div>

版本2

这绝对不是完美的工作,并且已经变得相当复杂,但我认为在计算更精确的hoverXhoverY时可以做得更好。这个版本做的是:

  • 当悬停装载器时存储鼠标位置
  • 隐藏加载器,以便通过x/y坐标选择文档中的元素
  • 滚动找到的元素或第一个可以滚动的父元素

当你悬停和滚动时,你会发现它并没有真正满足用户的期望,但最终你可以看到一切。为了使它更好,在存储或使用hoverX/hoverY时,应该考虑已经滚动的元素。

$(document).ready(function(){
    $("#loader").css('height', $('#content').height());
    $("#loader").css('width', $('#content').width());
    $("#loader-scroll").css('height', '2000'); // TODO iterate the 'content' div and calculate needed height
});

$('#loader').on('mouseover', function(event){
    $(this).data('hoverX', event.clientX);
  $(this).data('hoverY', event.clientY);
});

$('#loader').on('scroll', function(event){
    event.preventDefault();
    let $loader = $(this);
    $loader.css('z-index', -1);
    
    let element = document.elementFromPoint($loader.data('hoverX'), $loader.data('hoverY'));
    $loader.css('z-index', 9999);
   console.log('element under pointer: ' + element.tagName + ' #' + element.getAttribute('id'));
   let scrolled = scrollFirstScrollable(element, document.getElementById('content'), event.target.scrollTop);
   if(scrolled === false){
    console.log('did not scroll background element');
   }else{
    console.log('scrolled: ' + element.tagName + ' #' + element.getAttribute('id'));
    }
});

function scrollFirstScrollable(element, lastElement, amount){
  while(element){
    if(element.scrollHeight <= element.clientHeight){
      if(element === lastElement){
        return false;
      }
        element = element.parentElement;
    }else{
      element.scrollTop = amount;
        return element;
    }
  }
}

$('#content, #content div, button').each(function(){

    console.log(this.tagName + ' #' + this.getAttribute('id') + ' - ' + this.getAttribute('style'));
  console.log({'clientHeight': this.clientHeight, 'scrollheight': this.scrollHeight});
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<div style="position: relative">
  <div id="loader"
    style="
      position: absolute;
      left: 0;
      right: 0;
      top: 0;
      bottom: 0;
      background-color: red;
      opacity: 0.4;
      z-index: 9999;
      overflow:auto;
       /* display:none; */
    "><div id="loader-scroll">
  </div></div>

  <div id="content" style="width:200px;">
  
  <div id="subcontent-1" style="height: 200px; overflow: auto">
    <div id="subcontent-1-1" style="height: 1000px; background-color: blue;">
      <button onclick="alert('YOU CANNOT TRIGGER ME!')">
        YOU CANNOT TRIGGER ME
      </button>
    </div>
    </div>
    <div id="subcontent-2" style="height: 200px; overflow: auto">
  
    <div id="subcontent-2-1" style="height: 1000px; background-color: yellow;">
      <button onclick="alert('YOU CANNOT TRIGGER ME!')">
        YOU CANNOT TRIGGER ME, TOO!
      </button>
    </div>
    
</div>
yk9xbfzb

yk9xbfzb6#

通过在可滚动项上添加一个几乎透明的覆盖层,可以轻松地禁用元素的所有指针事件

.foo {
  position: relative;
}
.foo::after {
  content: "";
  position: absolute;
  top: 0; left: 0;
  right: 0; bottom: 0;
  background: red;
  opacity: 0.1;
  z-index: 9999;
}
<div style="display: flex;">
<div style="position: relative; display: flex">
  <div
    style="
      position: absolute;
      left: 0;
      right: 0;
      top: 0;
      bottom: 0;
      background-color: blue;
      opacity: 0.1;
      pointer-events: none;
    "
  ></div>

  <div style="width: 200px; height: 200px; overflow: auto">
    <div style="height: 1000px" class="foo">
      <button onclick="alert('YOU CANNOT TRIGGER ME!')">
        YOU CANNOT TRIGGER ME
      </button>
    </div>
  </div>
  <div style="width: 200px; height: 200px; overflow: auto">
    <div style="height: 1000px" class="foo">
      <button onclick="alert('YOU CANNOT TRIGGER ME!')">
        YOU CANNOT TRIGGER ME, TOO!
      </button>
    </div>
  </div>
</div>

<div style="position: relative; display: flex">
  <div style="width: 200px; height: 200px; overflow: auto">
    <div style="height: 1000px">
      <button onclick="alert('Im still useable!')">
        I'm still useable
      </button>
    </div>
  </div>
</div>
</div>
qlzsbp2j

qlzsbp2j7#

这不是一个完美的解决方案,但我不知道你会在哪里使用它。因此,或者,您可以计算是否在.blocked下找到元素,如果它没有滚动,则将pointer-events: none;添加到它。
P.S.很可能,这不是一个理想的解决方案。同样在这个例子中,我添加了(可选的).blocked-wrapp.blocked-exclude类,以避免遍历所有元素。

const disabledElements = (blockedClass, wrappClass, excludeClass) => {
  const { left, top, width, height } = document.querySelector(`.${blockedClass}`).getBoundingClientRect();
  const selector = wrappClass ? `.${wrappClass} *` : '*';
  document.querySelectorAll(selector).forEach(el => {
    if (
         el.classList.contains(`${blockedClass}`)
      || el.querySelector(`.${blockedClass}`)
      || el.classList.contains(excludeClass)
      || el.closest(`.${excludeClass}`)
    ){
      return;
    }
    el.style.removeProperty('pointer-events');
    const computedStyle = window.getComputedStyle(el);
    if (computedStyle.overflow === 'auto' || computedStyle.overflow === 'scroll') {
      return;
    }
    const { left: l, top: t, width: w, height: h } = el.getBoundingClientRect();
    if (l >= left && l + w <= left + width && t >= top && t + h <= top + height) {
      el.style.pointerEvents = 'none';
    }
  })
}

disabledElements('blocked', 'blocked-wrapp', 'blocked-exclude');
window.onresize = () => disabledElements('blocked', 'blocked-wrapp', 'blocked-exclude');
<div
  class="blocked-wrapp"
  style="display: flex;"
>
<div style="position: relative; display: flex">
  <div
    class="blocked"
    style="
      position: absolute;
      left: 0;
      right: 0;
      top: 0;
      bottom: 0;
      background-color: white;
      opacity: 0.5;
      pointer-events: none;
    "
  ></div>

  <div style="width: 200px; height: 200px; overflow: auto;">
    <div style="height: 1000px">
      <button onclick="alert('YOU CANNOT TRIGGER ME!')">
        YOU CANNOT TRIGGER ME
      </button>
    </div>
  </div>
  <div style="width: 200px; height: 200px; overflow: auto">
    <div style="height: 1000px">
      <button onclick="alert('YOU CANNOT TRIGGER ME!')">
        YOU CANNOT TRIGGER ME, TOO!
      </button>
    </div>
  </div>
</div>

<div style="position: relative; display: flex" class="blocked-exclude">
  <div style="width: 200px; height: 200px; overflow: auto">
    <div style="height: 1000px">
      <button onclick="alert('Im still useable!')">
        I'm still useable
      </button>
    </div>
  </div>
</div>
</div>

相关问题