我正在构建/尝试构建自己的小型框架。
最终,我将元素的值传递给keypress方法,它可以工作,但问题是我始终拥有我的初始值“test”。
那么为什么console.log没有显示我更新后的文本?
最终,我将元素的值传递给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>