首页 > 解决方案 > 按键事件后输入值不变

问题描述

我正在构建/尝试构建自己的小框架。

好吧,最后我将元素的值传递给 keypress 方法,它可以工作,但问题是我的初始值总是test.

那么为什么不console.log显示我更新的文本?

let initialProps = {
   change(text) {
      console.log(text);
   }
}

document.querySelectorAll("[keypress]").forEach(element => {
  this.keyHandler(element);
});
function keyHandler(element) {
  let handler = element.attributes.keypress.nodeValue;

  element.addEventListener(
    "keypress",
    initialProps[handler].bind(
      null,
      element.nodeName === "INPUT" ? element.value : undefined
    )
  );
}
<input keypress="change" value="test" type="text" />

这是完整的代码

setTimeout(()=>{
  new Tinyflow({
    name: "Max",
    age: 21,
    state: true,
    text: "",
    myTrans: "slide",
    increaseAge() {
      this.age++
    },
    toggle() {
      this.state = !this.state
    },
    changedText(text){
      this.text = text;
    },
    observers: {
      age(oldVal) {   
      }
    }
  });
})








class Tinyflow {
  constructor(properties) {
    console.time();
    this.reactivityHandler = {
      get: function(obj, prop) {
        return obj[prop];
      }.bind(this),
      set: function(obj, prop, newVal) {
        let oldVal = obj[prop];
        obj[prop] = newVal;
        if (prop in this.reactiveProps["observers"]) {
          this.reactiveProps["observers"][prop].call(
            this.reactiveProps,
            oldVal
          );
        }

        let i = this.reactiveElements.findIndex(el => el.var === "$" + prop);
        if (i !== -1) {
          this.renderSingleNode(i, newVal);
        }

        if (prop in this.showElements) {
          this.updateVisibility(this.showElements[prop], newVal);
        }
      }.bind(this)
    };
    if ("observers" in properties) {
      if (typeof properties["observers"] !== "object")
        throw Error("Observers needs to be an Object");
      for (let prop in properties["observers"]) {
        if (typeof properties["observers"][prop] !== "function")
          throw Error("An property inside observers is not an method: " + prop);
        if (!(prop in properties))
          throw Error(
            "Observer " +
              prop +
              " has nothing to observer. Add an property to observe"
          );
      }
    }
    this.initialProps = properties;
    this.reactiveProps = new Proxy(this.initialProps, this.reactivityHandler);
    this.reactiveElements = [];
    this.showElements = {};
    this.listenerRemover = null;
    this.collectHandlers();
    this.collectVariables();
    this.renderAllNodes();
    this.collectShowElements();
    console.timeEnd();
  }
  updateVisibility(elements, state) {
    if (state) {
      elements.forEach(elementProperties => {
        let { displayProperty, element } = elementProperties;
        let cssClassAvailable = element.attributes.trans;

        if (cssClassAvailable) {
          element.removeEventListener("transitionend", this.listenerRemover);
          let cssClass = this.reactiveProps[cssClassAvailable.nodeValue];
          setTimeout(() => {
            element.classList.remove(cssClass);
          });
          element.style.setProperty("display", displayProperty);
        } else {
          element.style.setProperty("display", displayProperty);
        }
      });
    } else {
      elements.forEach(elementProperties => {
        let { element } = elementProperties;
        let cssClassAvailable = element.attributes.trans;

        if (cssClassAvailable) {
          let cssClass = this.reactiveProps[cssClassAvailable.nodeValue];

          element.classList.add(cssClass);
          function removeListener() {
            element.style.setProperty("display", "none");
            element.removeEventListener("transitionend", this.listenerRemover);
          }
          this.listenerRemover = removeListener;
          element.addEventListener("transitionend", this.listenerRemover);
        } else {
          element.style.setProperty("display", "none");
        }
      });
    }
  }
  collectShowElements() {
    document.querySelectorAll("[show]").forEach(element => {
      let attributes = element.attributes;
      let stateVar = attributes.show.nodeValue;

      let displayProperty =
        window.getComputedStyle(element).getPropertyValue("display") === "none"
          ? "block"
          : window.getComputedStyle(element).getPropertyValue("display");

      if (this.showElements[stateVar]) {
        this.showElements[stateVar].push({
          element,
          displayProperty
        });
        if (!this.initialProps[stateVar]) {
          element.style.setProperty("display", "none");
        }
        return;
      }
      this.showElements[stateVar] = [
        {
          element,
          displayProperty
        }
      ];
      if (!this.initialProps[stateVar]) {
        element.style.setProperty("display", "none");
      }
    });
  }
  renderSingleNode(index, newVal) {
    let elements = this.reactiveElements;
    let value = elements[index].varValue;
    if (value === newVal) return;
    elements[index].varValue = newVal;

    elements[index].reference.forEach(ref => {
      let original = ref.originalText;
      ref.vars.forEach(vari => {
        let varWithoutDollar = vari.replace("$", "");
        let regex = new RegExp("\\" + vari, "g");
        if (varWithoutDollar === newVal) {
          original = original.replace(regex, newVal);
          return;
        }

        original = original.replace(regex, this.initialProps[varWithoutDollar]);
      });
      ref.element.innerHTML = original;
    });
  }
  renderAllNodes() {
    this.reactiveElements.forEach(element => {
      let variable = element.var;
      let varWithoutDollar = element.var.replace("$", "");

      element.reference.forEach(ref => {
        let regex = new RegExp("\\" + variable, "g");
        ref.element.innerHTML = ref.element.innerHTML.replace(
          regex,
          this.initialProps[varWithoutDollar]
        );
      });
    });
  }
  collectVariables() {
    document.querySelectorAll("[reactive]").forEach(element => {
      let vars = element.innerHTML.match(/(\$\w+)/g).reduce((a, v) => {
        if (!a.includes(v)) {
          a.push(v);
        }
        return a;
      }, []);

      vars.forEach(variable => {
        let i = this.reactiveElements.findIndex(el => el.var === variable);
        if (i === -1) {
          this.reactiveElements.push({
            reference: [{ element, originalText: element.innerHTML, vars }],
            var: variable,
            varValue: null
          });
          return;
        }
        this.reactiveElements[i].reference.push({
          element,
          originalText: element.innerHTML,
          vars
        });
      });
    });
  }

  clickHandler(element) {
    let handler = element.attributes.click.nodeValue;
    if (!this.initialProps[handler]) return;
    element.addEventListener(
      "click",
      this.initialProps[handler].bind(this.reactiveProps)
    );
  }
  changeHandler(element) {
    let handler = element.attributes.change.nodeValue;
    if (!this.initialProps[handler]) return;
    element.addEventListener(
      "change",
      this.initialProps[handler].bind(this.reactiveProps)
    );
  }
  //problem down here ------------------------------------------
  keyHandler(element) {
    let handler = element.attributes.keypress.nodeValue;
    if (!this.initialProps[handler]) return;

    element.addEventListener(
      "keypress",
      this.initialProps[handler].bind(
        this.reactiveProps,
        element.nodeName === "INPUT" ? element.value : undefined
      )
    );
  }

  collectHandlers() {
    document.querySelectorAll("[click]").forEach(element => {
      this.clickHandler(element);
    });
    document.querySelectorAll("[change]").forEach(element => {
      this.changeHandler(element);
    });
    document.querySelectorAll("[keypress]").forEach(element => {
      this.keyHandler(element);
    });
  }
}
#testapp {
    height: 200px;
    width: 200px;
    background: red;

  }

  .box {
    height: 300px;
    width: 200px;
    position: fixed;
    background: green;
    transition: left 500ms;
    transform: translateY(-50%);
    top: 50%;
    left: 0;
    height: 20vh;
  }

  .slide {
    left: -200px;
  }
<body>
  <div id="box">
    <p reactive>Hello. My name is: $name. I am $age old</p>
    <p reactive>my text: $text </p>
    <button click="increaseAge">Increase Age</button>
    <button click="toggle">Toggle</button>
    <input value="sdf" keypress="changedText" type="text">
    <div show="state" trans="myTrans" class="box"></div>
  </div>
</body>

更新:谢谢你们这里是工作代码:

setTimeout(()=>{
  new Tinyflow({
    name: "Max",
    age: 21,
    state: true,
    text: "",
    myTrans: "slide",
    increaseAge() {
      this.age++
    },
    toggle() {
      this.state = !this.state
    },
    changedText(text){
      this.text = text;
    },
    observers: {
      age(oldVal) {   
      }
    }
  });
})








class Tinyflow {
  constructor(properties) {
    console.time();
    this.reactivityHandler = {
      get: function(obj, prop) {
        return obj[prop];
      }.bind(this),
      set: function(obj, prop, newVal) {
        let oldVal = obj[prop];
        obj[prop] = newVal;
        if (prop in this.reactiveProps["observers"]) {
          this.reactiveProps["observers"][prop].call(
            this.reactiveProps,
            oldVal
          );
        }

        let i = this.reactiveElements.findIndex(el => el.var === "$" + prop);
        if (i !== -1) {
          this.renderSingleNode(i, newVal);
        }

        if (prop in this.showElements) {
          this.updateVisibility(this.showElements[prop], newVal);
        }
      }.bind(this)
    };
    if ("observers" in properties) {
      if (typeof properties["observers"] !== "object")
        throw Error("Observers needs to be an Object");
      for (let prop in properties["observers"]) {
        if (typeof properties["observers"][prop] !== "function")
          throw Error("An property inside observers is not an method: " + prop);
        if (!(prop in properties))
          throw Error(
            "Observer " +
              prop +
              " has nothing to observer. Add an property to observe"
          );
      }
    }
    this.initialProps = properties;
    this.reactiveProps = new Proxy(this.initialProps, this.reactivityHandler);
    this.reactiveElements = [];
    this.showElements = {};
    this.listenerRemover = null;
    this.collectHandlers();
    this.collectVariables();
    this.renderAllNodes();
    this.collectShowElements();
    console.timeEnd();
  }
  updateVisibility(elements, state) {
    if (state) {
      elements.forEach(elementProperties => {
        let { displayProperty, element } = elementProperties;
        let cssClassAvailable = element.attributes.trans;

        if (cssClassAvailable) {
          element.removeEventListener("transitionend", this.listenerRemover);
          let cssClass = this.reactiveProps[cssClassAvailable.nodeValue];
          setTimeout(() => {
            element.classList.remove(cssClass);
          });
          element.style.setProperty("display", displayProperty);
        } else {
          element.style.setProperty("display", displayProperty);
        }
      });
    } else {
      elements.forEach(elementProperties => {
        let { element } = elementProperties;
        let cssClassAvailable = element.attributes.trans;

        if (cssClassAvailable) {
          let cssClass = this.reactiveProps[cssClassAvailable.nodeValue];

          element.classList.add(cssClass);
          function removeListener() {
            element.style.setProperty("display", "none");
            element.removeEventListener("transitionend", this.listenerRemover);
          }
          this.listenerRemover = removeListener;
          element.addEventListener("transitionend", this.listenerRemover);
        } else {
          element.style.setProperty("display", "none");
        }
      });
    }
  }
  collectShowElements() {
    document.querySelectorAll("[show]").forEach(element => {
      let attributes = element.attributes;
      let stateVar = attributes.show.nodeValue;

      let displayProperty =
        window.getComputedStyle(element).getPropertyValue("display") === "none"
          ? "block"
          : window.getComputedStyle(element).getPropertyValue("display");

      if (this.showElements[stateVar]) {
        this.showElements[stateVar].push({
          element,
          displayProperty
        });
        if (!this.initialProps[stateVar]) {
          element.style.setProperty("display", "none");
        }
        return;
      }
      this.showElements[stateVar] = [
        {
          element,
          displayProperty
        }
      ];
      if (!this.initialProps[stateVar]) {
        element.style.setProperty("display", "none");
      }
    });
  }
  renderSingleNode(index, newVal) {
    let elements = this.reactiveElements;
    let value = elements[index].varValue;
    if (value === newVal) return;
    elements[index].varValue = newVal;

    elements[index].reference.forEach(ref => {
      let original = ref.originalText;
      ref.vars.forEach(vari => {
        let varWithoutDollar = vari.replace("$", "");
        let regex = new RegExp("\\" + vari, "g");
        if (varWithoutDollar === newVal) {
          original = original.replace(regex, newVal);
          return;
        }

        original = original.replace(regex, this.initialProps[varWithoutDollar]);
      });
      ref.element.innerHTML = original;
    });
  }
  renderAllNodes() {
    this.reactiveElements.forEach(element => {
      let variable = element.var;
      let varWithoutDollar = element.var.replace("$", "");

      element.reference.forEach(ref => {
        let regex = new RegExp("\\" + variable, "g");
        ref.element.innerHTML = ref.element.innerHTML.replace(
          regex,
          this.initialProps[varWithoutDollar]
        );
      });
    });
  }
  collectVariables() {
    document.querySelectorAll("[reactive]").forEach(element => {
      let vars = element.innerHTML.match(/(\$\w+)/g).reduce((a, v) => {
        if (!a.includes(v)) {
          a.push(v);
        }
        return a;
      }, []);

      vars.forEach(variable => {
        let i = this.reactiveElements.findIndex(el => el.var === variable);
        if (i === -1) {
          this.reactiveElements.push({
            reference: [{ element, originalText: element.innerHTML, vars }],
            var: variable,
            varValue: null
          });
          return;
        }
        this.reactiveElements[i].reference.push({
          element,
          originalText: element.innerHTML,
          vars
        });
      });
    });
  }

  clickHandler(element) {
    let handler = element.attributes.click.nodeValue;
    if (!this.initialProps[handler]) return;
    element.addEventListener(
      "click",
      this.initialProps[handler].bind(this.reactiveProps)
    );
  }
  changeHandler(element) {
    let handler = element.attributes.change.nodeValue;
    if (!this.initialProps[handler]) return;
    element.addEventListener(
      "change",
      this.initialProps[handler].bind(this.reactiveProps)
    );
  }
  //problem down here ------------------------------------------
  keyHandler(element) {
    let handler = element.attributes.keypress.nodeValue;
    if (!this.initialProps[handler]) return;

    element.addEventListener(
      "keyup",(event) =>
      this.initialProps[handler].call(
        this.reactiveProps,
        element.nodeName === "INPUT" ? event.target.value : undefined
      )
    );
  }

  collectHandlers() {
    document.querySelectorAll("[click]").forEach(element => {
      this.clickHandler(element);
    });
    document.querySelectorAll("[change]").forEach(element => {
      this.changeHandler(element);
    });
    document.querySelectorAll("[keypress]").forEach(element => {
      this.keyHandler(element);
    });
  }
}
#testapp {
    height: 200px;
    width: 200px;
    background: red;

  }

  .box {
    height: 100px;
    width: 100px;
    position: fixed;
    background: green;
    transition: left 500ms;
    transform: translateY(-50%);
    top: 70%;
    left: 0;
    height: 20vh;
  }

  .slide {
    left: -200px;
  }
<body>
  <div id="box">
    <p reactive>Hello. My name is: $name. I am $age old</p>
    <p reactive>my text: $text </p>
    <button click="increaseAge">Increase Age</button>
    <button click="toggle">Toggle</button>
    <input value="sdf" keypress="changedText" type="text">
    <div show="state" trans="myTrans" class="box"></div>
  </div>
</body>

标签: javascript

解决方案


为什么它不起作用?

因为 element.value 将始终返回在 html 代码中初始化的值

解决方案

  • 将 event.target.value 传递给函数。
  • 不要'绑定,调用事件处理程序

奖金

订阅“keyup”事件而不是“keypress”以获得更好的状态同步

let initialProps = {
   change(text) {
      console.log(text);
   }
}

document.querySelectorAll("[keypress]").forEach(element => {
  this.keyHandler(element);
});
function keyHandler(element) {
  let handler = element.attributes.keypress.nodeValue;

  element.addEventListener(
    "keyup",
    (event) => {
      initialProps[handler].call(
      null,
      element.nodeName === "INPUT" ? event.target.value : undefined
      )
    }
  );
}
<input keypress="change" value="test" type="text" />


推荐阅读