三行代码  ›  专栏  ›  技术社区  ›  WillWillington

如何在Javascript中创建元素包装器?

  •  1
  • WillWillington  · 技术社区  · 2 月前

    出于纯粹的教育和好奇目的,我试图创建一个元素包装器对象,它允许我将自己的属性和方法附加到一个元素上。我试图模拟的行为基本上是这样的:

    // get a button element to wrap
    const button = document.querySelector('button');
    
    // some function that wraps new properties/methods around a given element
    function wrap(element) {
        this.customName = 'John';
        this.customAge = 100;
    
        this.printName = function() {
            console.log(this.customName);
        }
    
        // ...
        // ...somehow inherit element fields...
        // ...
    }
    
    // wrap the button element
    const customElement = new wrap(button);
    
    // custom behavior:
    console.log(customElement.customAge) // output => 100
    customElement.printName() // output => 'John'
    
    // legacy behavior
    console.log(customElement.clientHeight) // output => client height
    customElement.remove() // => should still call 'remove' on the element
    

    因此,在这里,我应该能够添加自己的方法/属性,但仍然可以正常访问原始字段。这可能吗?

    我在这里使用构造函数作为示例,只是为了演示预期的行为,但我不知道这是否与解决方案相关。我是Javascript新手,我已经对原型和类做了大量研究,但我仍然不知道我会采取什么方法。

    编辑: 正如Brad在评论中指出的那样,我还使用类尝试了这个实现:

    class MyButton extends HTMLButtonElement {
        constructor() {
            super();
            this.customName = 'John';
            this.customAge = 100;
        }
    
        printName() {
            console.log(this.customName);
        }
    }
    
    const myBtn = new MyButton();
    

    但这导致了错误: Uncaught TypeError: Illegal constructor

    2 回复  |  直到 2 月前
        1
  •  1
  •   mahdilamb    2 月前

    我没有测试过这个,但可能是这样的:

    // get a button element to wrap
    const button = document.querySelector('button');
    
    // some function that wraps new properties/methods around a given element
    function wrap(element) {
        Object.defineProperties(element, {
            customName: {value:"John"},
            customAge: {value:100},
            printName:{value: () => console.log(element.customName)}
        })
        return element
    }
    
    // wrap the button element
    const customElement = wrap(button);
    
    // custom behavior:
    console.log(customElement.customAge) // output => 100
    customElement.printName() // output => 'John'
    
    // legacy behavior
    console.log(customElement.clientHeight) // output => client height
    customElement.remove() // => should still call 'remove' on the element
    <button>Hello world!</button>
        2
  •  0
  •   vanowm    2 月前

    可以使用的另一种方法是将元素包装到 proxy() 这将允许在属性不存在时返回自定义数据,并在属性更改时发送通知:

    const customElement = function (element, properties = {})
    {
      this.element = element;
      this.customName = 'John';
      this.customAge = 100;
    
      this.printName = function() {
          console.log(this.customName);
      }
    
      //override default properties
      for(let i in properties)
      {
        if (i in element)
          element[i] = properties[i];
        else
          this[i] = properties[i];
      }
    
      return new Proxy(this, {
        get(target, prop)
        {
          if (prop in target.element) //is property exists in element?
          {
            if (target.element[prop] instanceof Function)
              return target.element[prop].bind(target.element);
    
            return target.element[prop];
          }
          else if (prop in target) //is property exists in our object?
            return target[prop];
          else
            return "unknown property"; //unknown property
        },
        set(target, prop, value, thisProxy)
        {
          const oldValue = thisProxy[prop];
    
          if (prop in target.element)
            target.element[prop] = value;
          else
            target[prop] = value;
          // send notification
          target.element.dispatchEvent(new CustomEvent("propertyChanged", {
            detail: {
              prop,
              oldValue,
              value
            }
          }));
        }
      });
    }
    
    
    const button = new customElement(document.createElement("button"), {customName: "Not John"});
    
    
    button.addEventListener("propertyChanged", e =>
    {
      console.log("property changed", e.detail);
    });
    
    
    button.printName();
    console.log("age:", button.customAge);
    console.log("height:", button.clientHeight);
    
    console.log("blah:", button.blah);
    
    button.blah = "ok";
    console.log("blah:", button.blah);