使用 dispatchEvent 来调度它,并等待 promise 解析



我有一个自定义构建模式,在显示它之后必须停止执行任何代码。对我来说,当我直接调用它时,这不是问题(我可以处理),但问题是当我必须在相关元素上使用 dispatchEvent 时。我在这里构建了基本示例:

const modal = document.getElementById("myModal");
const btn = document.getElementById("myBtn");
const span = document.getElementsByClassName("close")[0];
btn.onclick = async function() {
modal.style.display = "block";
return new Promise(resolve => {
span.onclick = async function() {
modal.style.display = "none";
return await true;
};
});
};
(async () => {
btn.style.color = "black";
const event = new Event('click');
await btn.dispatchEvent(event);

btn.style.color = "red";

})();
.modal {
display: none; 
position: fixed; 
z-index: 1; 
left: 0;
top: 0;
width: 100%; 
height: 100%; 
overflow: auto; 
background-color: rgb(0,0,0);
background-color: rgba(0,0,0,0.4);
}
.modal-content {
background-color: #fefefe;
margin: 15% auto;
padding: 20px;
border: 1px solid #888;
width: 80%; 
}

.close {
color: #aaa;
float: right;
font-size: 28px;
font-weight: bold;
}
.close:hover,
.close:focus {
color: black;
text-decoration: none;
cursor: pointer;
}
<button id="myBtn">Open Modal and wait (Note: this should't be red before closing modal)</button>
<div id="myModal" class="modal">
<div class="modal-content">
<span class="close">&times;</span>
<p>Some text in the Modal..</p>
</div>
</div>

如何等待btn.dispatchEvent(event)

那里有几个问题:

  1. async函数用作 DOM 事件侦听器没有任何意义。DOM 对返回的承诺不执行任何操作。
  2. 避免显式 promise 构造反模式 —async函数始终返回 promise,除非需要显式resolvereject函数,否则无需构造 promise。
  3. 您需要在 DOM 事件系统之外进行协调,因为dispatchEvent不会返回您正在创建的承诺。

例如,您可以像这样设置modalPromisemodalResolve

let modalPromise = Promise.resolve();
let modalResolve = null;

。然后在显示模态时像这样更新:

modalPromise = modalPromise.then(() => {
return new Promise((resolve) => {
modalResolve = resolve;
});
});

然后你在显示模态后await

这里有一个想法(我也更新了它以使用现代事件处理,并且不使用var-var现代JavaScript代码中没有地位):

const modal = document.getElementById("myModal");
const btn = document.getElementById("myBtn");
const span = document.getElementsByClassName("close")[0];
let modalPromise = Promise.resolve();
let modalResolve = () => {};
span.addEventListener("click", function () {
modal.style.display = "none";
if (modalResolve) {
modalResolve();
modalResolve = null;
}
});
btn.addEventListener("click", function () {
modal.style.display = "block";
modalPromise = modalPromise.then(() => {
return new Promise((resolve) => {
modalResolve = resolve;
});
});
});
(async () => {
btn.style.color = "black";
const event = new Event("click");
btn.dispatchEvent(event);
await modalPromise;
btn.style.color = "red";
})();
.modal {
display: none; 
position: fixed; 
z-index: 1; 
left: 0;
top: 0;
width: 100%; 
height: 100%; 
overflow: auto; 
background-color: rgb(0,0,0);
background-color: rgba(0,0,0,0.4);
}
.modal-content {
background-color: #fefefe;
margin: 15% auto;
padding: 20px;
border: 1px solid #888;
width: 80%; 
}

.close {
color: #aaa;
float: right;
font-size: 28px;
font-weight: bold;
}
.close:hover,
.close:focus {
color: black;
text-decoration: none;
cursor: pointer;
}
<button id="myBtn">Open Modal and wait (Note: this should't be red before closing modal)</button>
<div id="myModal" class="modal">
<div class="modal-content">
<span class="close">&times;</span>
<p>Some text in the Modal..</p>
</div>
</div>

这只是一个草图,您需要针对诸如尝试显示模态两次重叠等问题对其进行强化。

两种可能性,第一种是 - 如果你不完全依赖于"dispatchEvent" - 使用可能的替换器方法,如下所示:

var modal = document.getElementById("myModal");
var btn = document.getElementById("myBtn");
var span = document.getElementsByClassName("close")[0];
const openModal = () => {
// This method will return a promise which we want to await,
// until the modal is closed again. The promise awaiting for
// close is called p.
const p = new Promise((resolve) => {
const onSpanClick = () => {
modal.style.display = "none";
span.removeEventListener('click', onSpanClick);
resolve();
};

const onClick = () => {
modal.style.display = "block";
span.addEventListener('click', onSpanClick);
btn.removeEventListener('click', onClick);
};
btn.addEventListener('click', onClick);
});
// We need to return a double promise here, as we need to leave the function and return a promise to wait on, BEFORE the click is executed.
return new Promise((resolve) => {
p.then(resolve);

// otherwise we would return the promise after the click
// was executed and our promise didn't had any chance to wait
const event = new Event('click');
btn.dispatchEvent(event);
});
};
(async () => {
btn.style.color = "black";
await openModal();
btn.style.color = "red";
})();
.modal {
display: none; 
position: fixed; 
z-index: 1; 
left: 0;
top: 0;
width: 100%; 
height: 100%; 
overflow: auto; 
background-color: rgb(0,0,0);
background-color: rgba(0,0,0,0.4);
}
.modal-content {
background-color: #fefefe;
margin: 15% auto;
padding: 20px;
border: 1px solid #888;
width: 80%; 
}

.close {
color: #aaa;
float: right;
font-size: 28px;
font-weight: bold;
}
.close:hover,
.close:focus {
color: black;
text-decoration: none;
cursor: pointer;
}
<button id="myBtn">Open Modal and wait (Note: this should't be red before closing modal)</button>
<div id="myModal" class="modal">
<div class="modal-content">
<span class="close">&times;</span>
<p>Some text in the Modal..</p>
</div>
</div>

另一种可能性是对"btn.dispatchEvent"进行黑客覆盖,以确保在"btn"实例上调用"dispatchEvent"时始终保证此行为,如下所示:

const oldDispatchEvent = btn.dispatchEvent.bind(btn);
btn.dispatchEvent = (event) => {
// analyze event if it is a click event and create promise p (as in the
//  "openModal" function in the snippet above (solution 1)

return new Promise((resolve) => {
p.then(resolve);
oldDispatchEvent(event);
});
// else
return oldDispatchEvent(event);
};

我个人不推荐第二种解决方案,因为它安静肮脏且非标准,这意味着头痛,几个月后您将再次查看您的代码。 :D

这是一个更简单的设置,它将在模式关闭后立即读取文本:

var modal = document.getElementById("myModal");
var btn = document.getElementById("myBtn");
var span = document.getElementsByClassName("close")[0];
console.clear();
btn.onclick = async function() {
modal.style.display = "block";
span.onclick = function() {
modal.style.display = "none";
btn.style.color = "red";
};
};
(async () => {
btn.style.color = "black";
const event = new Event('click');
btn.dispatchEvent(event);

})();

var modal = document.getElementById("myModal");
var btn = document.getElementById("myBtn");
var span = document.getElementsByClassName("close")[0];
console.clear();
btn.onclick = async function() {
modal.style.display = "block";
span.onclick = function() {
modal.style.display = "none";
btn.style.color = "red";
return true;
};
};
(async () => {
btn.style.color = "black";
const event = new Event('click');
btn.dispatchEvent(event);

})();
.modal {
display: none; 
position: fixed; 
z-index: 1; 
left: 0;
top: 0;
width: 100%; 
height: 100%; 
overflow: auto; 
background-color: rgb(0,0,0);
background-color: rgba(0,0,0,0.4);
}
.modal-content {
background-color: #fefefe;
margin: 15% auto;
padding: 20px;
border: 1px solid #888;
width: 80%; 
}

.close {
color: #aaa;
float: right;
font-size: 28px;
font-weight: bold;
}
.close:hover,
.close:focus {
color: black;
text-decoration: none;
cursor: pointer;
}
<button id="myBtn">Open Modal and wait (Note: this should't be red before closing modal)</button>
<div id="myModal" class="modal">
<div class="modal-content">
<span class="close">&times;</span>
<p>Some text in the Modal..</p>
</div>
</div>

最新更新