【问题标题】:Class events bind and unbind类事件绑定和解除绑定
【发布时间】:2020-03-22 08:21:12
【问题描述】:

一切正常,除了我不知道如何取消绑定 stop() 中的事件。

代码更新

class Resizable {
  go(drag_el, resize_el) {
    this.resize_el = resize_el;

    drag_el.addEventListener("mousedown", () => {
      window.addEventListener("mousemove", this.resize.bind(this));
      window.addEventListener("mouseup", () => {
        window.removeEventListener("mousemove", this.resize.bind(this));
        window.removeEventListener("mouseup", this.resize.bind(this));
      });
    });
  }

  resize(e) {
    this.resize_el.style.width = e.clientX - this.resize_el.offsetLeft + "px";
  }
}

一些注意事项

  • 我更喜欢将类的东西保留在类里面,在它之外没有额外的var
  • 我也接受 ES5 和 ES6。
  • 我读到我可以做到window.mousemove.bind(() => { // Code });,但我没有得到它的工作。

【问题讨论】:

    标签: javascript class events bind addeventlistener


    【解决方案1】:

    对于这样的场景,我通常更喜欢将对象作为侦听器直接传递给addEventListener:它使一切变得更容易 IMVHO。你只需要实现handleEvent 方法:

    class Resizable {
      // Keeping a reference to the elements it's easier for the events handling
      constructor(drag_el, resize_el) {
        this.drag_el = drag_el;
        this.resize_el = resize_el;
        // The entry point event handler is added in the constructor
        // to avoid to add multiple listeners (e.g. in the original
        // code we could have called `go` method multiple times).
        this.drag_el.addEventListener("mousedown", () => {
          // Here we add the object itself as event handler
          window.addEventListener("mousemove", this);
          window.addEventListener("mouseup", this);
        });
      }
    
      resize(e) {
        this.resize_el.style.width = e.clientX - this.resize_el.offsetLeft + "px";
      }
    
      // Here we're removing the object itself as event handler
      stop() {
        window.removeEventListener("mousemove", this, false);
        window.removeEventListener("mouseup", this, false);
      }
    
      // Here we're handling the events: since we added the object
      // itself, `this` is still pointing to our instance of `Resizable`
      // so we can call its methods.
      handleEvent(event) {
        switch (event.type) {
            case "mousemove": 
              this.resize(event);
              break;
            case "mouseup":
              this.stop();
              break;
        }
      }
    }
    

    这种方法大大减少了绑定和箭头函数的使用。

    在这里您可以找到运行此代码的codepen demo

    【讨论】:

    • 您的 codepen 似乎按预期工作。它看起来也非常像我正在尝试构建的东西。明天将对其进行更多测试。谢谢!
    • 我有两个很好的答案,你的和另一个。我选择了另一个,但很难选择最好的一个。如果您对最终结果感兴趣,我已将您的解决方案和获胜者解决方案组合成一个自己的答案。 stackoverflow.com/a/59069272/148496
    【解决方案2】:

    您无法删除 stop 函数中的侦听器,因为您不知道它们的引用。

    Mousemove 事件不调用resize 函数,它调用匿名函数

    e => {
      this.resize(resize_el, e);
    }
    

    您可以将匿名函数的地址保存在变量中并用于删除侦听器,如下所示

    class Resizable {
      constructor() {
        this.mouseMoveHandler = (e) => {
          this.resize(resize_el, e);
        };
    
        this.mouseDownHandler = () => {
          window.addEventListener("mousemove", this.mouseMoveHandler);
        }
    
        this.mouseUpHandler = () => {
          window.removeEventListener("mousemove", this.mouseMoveHandler);
        }
      }
    
      go(drag_el, resize_el) {
        drag_el.addEventListener("mousedown", this.mouseDownHandler);
    
        window.addEventListener("mouseup", this.mouseUpHandler);
      }
    
      resize(resize_el, e) {
        resize_el.style.width = e.clientX - resize_el.offsetLeft + "px";
      }
    
      stop() {
        window.removeEventListener("mousemove", this.mouseMoveHandler); // ?
        // most likely you would like remove listeners from this.go
      }
    }
    

    您可以在this article on Medium [removeEventListener() and anonymous function]找到更多信息

    UPD(问题更新后)

    .bind() 每次都会创建 NEW 函数,所以你仍然无法移除事件监听器。

    在这里检查我的小提琴:https://jsfiddle.net/nvfcq80z/5/

    【讨论】:

    • 这不起作用,因为我需要传递一个参数。为此,我需要绑定。我已经更新了我的代码。还是不行。
    • @JensTörnell 我准备了小提琴,你可以检查一下jsfiddle.net/nvfcq80z/5
    • 我测试过,效果很好。我注意到的一件事是不再需要 stop() 方法。 jsfiddle.net/gkvnxL9b
    • 很难选出赢家,但您赢得了公认的答案。作为补充,我已将您的解决方案和另一个好的解决方案组合成一个自己的答案,以防您对最终结果感兴趣。 stackoverflow.com/a/59069272/148496
    【解决方案3】:

    saveli 说的是真的,你必须在移除监听器时调用相同的函数引用(指向在监听器内部调用的相同函数),如果你想要更多解释,请确保你可以问

    class Resizable {
       go(drag_el, resize_el) {
          this.resize_el=resize_el;
          drag_el.addEventListener("mousedown", () => {
             $(window).bind("mousemove", this.resize);
             $(window).bind("mouseup", this.stop);
          });
       }
    
       resize= (e) =>{
             this.resize_el.style.width = e.clientX - this.resize_el.offsetLeft + "px";;
        }
       stop =(event) =>{
                    $(window).unbind(event,this.stop);
                    $(window).unbind("mousemove",this.resize); 
        }
    
    }
    

    只用js编辑这个,不用jquery

    class Resizable {
       go(drag_el, resize_el) {
          this.resize_el=resize_el;
          drag_el.addEventListener("mousedown", () => {
             window.addEventListener("mousemove", this.resize);
             window.addEventListener("mouseup", this.stop);
          });
       }
    
       resize= (e) =>{
             this.resize_el.style.width = e.clientX - this.resize_el.offsetLeft + "px";
       }
      stop =(event) =>{ 
          window.removeEventListener(event,this.stop);
          window.removeEventListener("mousemove",this.resize);
       }
    
    }
    

    【讨论】:

    • 看起来像 jQuery,我用的是 Javascript。
    • 现在你有一个 jquery 另一个只有一个 js
    • 它没有,但我还有两个其他答案有效。无论如何感谢您的努力。
    • 我测试过但没问题
    【解决方案4】:

    我已经接受了我最喜欢的有效答案。不过我认为@SaveliTomac 和@ZER0 的组合是最好的答案。

    因此,至少在我看来,我从两个世界中挑选了最好的。

    https://jsfiddle.net/nhLjozm7/

    HTML

    <aside class="sidebar1">
      <div class="sidebar1-resize"></div>
    </aside>
    
    <div class="sidebar2">
      <div class="sidebar2-resize"></div>
    </div>
    

    JS

    class Resizable {
      constructor(drag_el, resize_el) {
        this.drag_el = drag_el;
        this.resize_el = resize_el;
    
        this.handlers();
        this.events();
      }
    
      handlers() {
        this.mouseMoveHandler = (e) => {
          this.resize(this.resize_el, e);
        };
    
        this.mouseDownHandler = () => {
          window.addEventListener("mousemove", this.mouseMoveHandler);
        }
    
        this.mouseUpHandler = () => {
          window.removeEventListener("mousemove", this.mouseMoveHandler);
        }
      }
    
      events() {
        this.drag_el.addEventListener("mousedown", this.mouseDownHandler);
        window.addEventListener("mouseup", this.mouseUpHandler);
      }
    
      resize(resize_el, e) {
        resize_el.style.width = e.clientX - resize_el.offsetLeft + "px";
      }
    }
    
    let drag1 = document.querySelector(".sidebar1-resize");
    let element1 = document.querySelector(".sidebar1");
    new Resizable(drag1, element1);
    
    let drag2 = document.querySelector(".sidebar2-resize");
    let element2 = document.querySelector(".sidebar2");
    new Resizable(drag2, element2);
    
    • 我喜欢@SaveliTomac 提供的更扁平的处理程序结构(没有事件嵌套)。
    • 我喜欢 @ZER0 将调整大小手柄放在一边而不是放在一边的方式。
    • 我喜欢@ZER0 和@SaveliTomac 解决方案如何支持多个可调整大小的区域。
    • 我喜欢@SaveliTomac 简洁易读的代码。
    • 我喜欢 @ZER0 直接将一组值放入构造函数中。
    • 我将所有处理程序包装到一个方法中。
    • 我将事件包装到一个方法中。
    • 我稍微更改了 HTML。
    • 我展示了两个可调整大小的元素。

    【讨论】:

    • 事实上,您希望在 drag_el 上取消 mousedown 并在 window 上取消 mouseup,因为这可能会导致内存泄漏。我又准备了小提琴:)jsfiddle.net/cqpux9yg你可以运行代码并点击结果页面,检查你的控制台后
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-07-05
    • 1970-01-01
    • 1970-01-01
    • 2012-03-17
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多