在 html 中直接使用 ES6 模块中定义的参数化函数
Use parameterised functions defined in ES6 module directly in html
嵌入在 HTML 脚本中的 ES6 模块内定义的函数对该脚本不可用。因此,如果您有如下语句:
<button onclick="doSomething();">Do something</button>
在你的 HTML 中,你的 doSomething() 函数位于嵌入在 HTML 脚本中的 ES6 模块中,当你 运行 脚本。
提出了解决当前问题的好方法,建议您通过修改 HTML 将您的功能“绑定”到按钮:
<button id="dosomethingbutton">Do something</button>
并使用模块本身创建链接:
document.getElementById('dosomethingbutton').addEventListener('click', doSomething);
这工作正常,但如果您的原始按钮更复杂一点并且已参数化怎么办?例如:
<button onclick="doSomething('withThisString');">Do Something with String</button>
“绑定”所能提供的最多似乎仅限于与事件相关的情况——我找不到将其与数据相关联的方法。我完全无法找到解决此问题的方法,我们将不胜感激。
我想补充一点,虽然这个问题可能看起来有点晦涩,但我认为迁移到 Firebase 9 的任何人都会感兴趣。除其他更改外,迁移还需要您移动 javascript 代码到 ES6 模块中(其中命名空间不能直接用于 HTML DOM),因此最简单的 HTML 很可能会立即遇到这些问题。欢迎提供建议。
This works fine, but what if your original button was a bit more sophisticated and was parameterised?
有几个解决方案:
一个data-*
属性:
<button id="the-button" data-string="withThisString">Do Something with String</button>
document.getElementById("the-button").addEventListener("click", function() {
doSomething(this.getAttribute("data-string"));
});
(更多内容见下文。)
或
绑定事件时绑定字符串
<button id="the-button">Do Something with String</button>
document.getElementById("the-button").addEventListener("click", () => {
doSomething("withThisString");
});
上面有很多变体,如果您将 doSomething
与具有不同字符串的多个按钮一起使用,您可以使用 class 和循环而不是 id
,但这是一般的想法。
关于 data-*
属性的事情:如果你愿意,你可以通过 data-*
属性和一个连接事物的函数使这个过程完全 HTML 驱动。例如,假设您有这些按钮:
<button data-click="doThisx@module1">Do This</button>
<button data-click="doThat@module2">Do That</button>
<button data-click="doTheOther@module3">Do The Other</button>
您可以使用一个可重复使用的函数来连接它们:
class EventSetupError extends Error {
constructor(element, msg) {
if (typeof element === "string") {
[element, msg] = [msg, element];
}
super(msg);
this.element = element;
}
}
export async function setupModuleEventHandlers(eventName) {
try {
const attrName = `data-${eventName}`;
const elements = [...document.querySelectorAll(`[${attrName}]`)];
await Promise.all(elements.map(async element => {
const attrValue = element.getAttribute(`data-${eventName}`);
const [fname, modname] = attrValue ? attrValue.split("@", 2) : [];
if (!fname || !modname) {
throw new EventSetupError(
element,
`Invalid '${attrName}' attribute "${attrValue}"`
);
}
// It's fine if we do import() more than once for the same module,
// the module loader will return the same module
const module = await import(`./${modname}.js`);
const fn = module[fname];
if (typeof fn !== "function") {
throw new EventSetupError(
element,
`Invalid '${attrName}': no '${fname}' on module '${modname}' or it isn't a function`
);
}
element.addEventListener(eventName, fn);
}));
} catch (error) {
console.error(error.message, error.element);
}
}
使用它来查找和连接点击处理程序:
import { setupModuleEventHandlers } from "./data-attr-event-setup.js";
setupModuleEventHandlers("click")
.catch(error => {
console.error(error.message, error.element);
});
它是一次性管道,但在 HTML 中为您提供相同的基于属性的体验(事件处理程序仍然可以从另一个 data-*
属性获取参数信息,或者您可以将其烘焙到你的设置功能)。 (该示例依赖于 dynamic import
, but that's supported by recent versions of all major browsers,并且在不同程度上依赖于捆绑器。
有几十种方法可以实现它,我不是在推广它,只是举一个例子,说明如果你愿意,你可以很容易地做到这一点。
但实际上,这就是 React、Vue、Ember、Angular、Lit 等库发挥作用的地方。
虽然T.J克劳德已经回答了这个问题,但我想我可能会补充一些难以挤入的观点作为评论。
一旦我深入了解我的 Firebase V9 转换,我开始发现“模块命名空间”问题的一些后果是相当深远的。如上所述,我最初的问题中引用的示例很容易处理,但我发现我还需要弄清楚如何处理“动态”HTML 以响应来自数据库的可变情况。在这种情况下,我的 javascript 最初会创建一个包含 HTML 块的字符串,例如:
realDiv = `
<div>
<button onclick = "function fn1 (param1a, param1b,...);">button1</button>
<button onclick = "function fn2 (param2a, param2b,...);">button2</button>
etc
</div>
`
然后将其放入应用程序 HTML 骨架中定义的“真正的” realdiv 中,并带有
document.getElementById("realdiv") = realDiv;
现在,由于上述原因,一旦 javascript 在模块中,这种安排就不再有效了。
我学会采用的模式(再次感谢 T.J Crowder)遵循以下原则:
realDiv = `
<div>
<button id = "button1" data-param1a="param1a" data-param1b="param1b";">button1</button>
<button id = "button2" data-param2a="param2a" data-param2b="param2b";">button2</button>
etc
</div>
`
然后我会像以前一样将生成的代码放入我的 HTML 框架中
document.getElementById("realdiv") = readlDiv;
现在您已经将代码嵌入到 DOM 中(假设我已经统计了您生成的按钮数量),我会为它们创建绑定javascript 的最后一点像这样:
for (let i = 0; i>buttonCount; i++) {
document.getElementById('button' + i).onclick = function() { fn1 (this.getAttribute('data-param1a'), this.getAttribute('data-param1b') };
etc
}
我发现当我需要让 onclick 启动许多不同的功能时,使用这种模式创建 onclicks 对于保持清晰度特别有帮助。
嵌入在 HTML 脚本中的 ES6 模块内定义的函数对该脚本不可用。因此,如果您有如下语句:
<button onclick="doSomething();">Do something</button>
在你的 HTML 中,你的 doSomething() 函数位于嵌入在 HTML 脚本中的 ES6 模块中,当你 运行 脚本。
<button id="dosomethingbutton">Do something</button>
并使用模块本身创建链接:
document.getElementById('dosomethingbutton').addEventListener('click', doSomething);
这工作正常,但如果您的原始按钮更复杂一点并且已参数化怎么办?例如:
<button onclick="doSomething('withThisString');">Do Something with String</button>
“绑定”所能提供的最多似乎仅限于与事件相关的情况——我找不到将其与数据相关联的方法。我完全无法找到解决此问题的方法,我们将不胜感激。
我想补充一点,虽然这个问题可能看起来有点晦涩,但我认为迁移到 Firebase 9 的任何人都会感兴趣。除其他更改外,迁移还需要您移动 javascript 代码到 ES6 模块中(其中命名空间不能直接用于 HTML DOM),因此最简单的 HTML 很可能会立即遇到这些问题。欢迎提供建议。
This works fine, but what if your original button was a bit more sophisticated and was parameterised?
有几个解决方案:
一个
data-*
属性:<button id="the-button" data-string="withThisString">Do Something with String</button>
document.getElementById("the-button").addEventListener("click", function() { doSomething(this.getAttribute("data-string")); });
(更多内容见下文。)
或
绑定事件时绑定字符串
<button id="the-button">Do Something with String</button>
document.getElementById("the-button").addEventListener("click", () => { doSomething("withThisString"); });
上面有很多变体,如果您将 doSomething
与具有不同字符串的多个按钮一起使用,您可以使用 class 和循环而不是 id
,但这是一般的想法。
关于 data-*
属性的事情:如果你愿意,你可以通过 data-*
属性和一个连接事物的函数使这个过程完全 HTML 驱动。例如,假设您有这些按钮:
<button data-click="doThisx@module1">Do This</button>
<button data-click="doThat@module2">Do That</button>
<button data-click="doTheOther@module3">Do The Other</button>
您可以使用一个可重复使用的函数来连接它们:
class EventSetupError extends Error {
constructor(element, msg) {
if (typeof element === "string") {
[element, msg] = [msg, element];
}
super(msg);
this.element = element;
}
}
export async function setupModuleEventHandlers(eventName) {
try {
const attrName = `data-${eventName}`;
const elements = [...document.querySelectorAll(`[${attrName}]`)];
await Promise.all(elements.map(async element => {
const attrValue = element.getAttribute(`data-${eventName}`);
const [fname, modname] = attrValue ? attrValue.split("@", 2) : [];
if (!fname || !modname) {
throw new EventSetupError(
element,
`Invalid '${attrName}' attribute "${attrValue}"`
);
}
// It's fine if we do import() more than once for the same module,
// the module loader will return the same module
const module = await import(`./${modname}.js`);
const fn = module[fname];
if (typeof fn !== "function") {
throw new EventSetupError(
element,
`Invalid '${attrName}': no '${fname}' on module '${modname}' or it isn't a function`
);
}
element.addEventListener(eventName, fn);
}));
} catch (error) {
console.error(error.message, error.element);
}
}
使用它来查找和连接点击处理程序:
import { setupModuleEventHandlers } from "./data-attr-event-setup.js";
setupModuleEventHandlers("click")
.catch(error => {
console.error(error.message, error.element);
});
它是一次性管道,但在 HTML 中为您提供相同的基于属性的体验(事件处理程序仍然可以从另一个 data-*
属性获取参数信息,或者您可以将其烘焙到你的设置功能)。 (该示例依赖于 dynamic import
, but that's supported by recent versions of all major browsers,并且在不同程度上依赖于捆绑器。
有几十种方法可以实现它,我不是在推广它,只是举一个例子,说明如果你愿意,你可以很容易地做到这一点。
但实际上,这就是 React、Vue、Ember、Angular、Lit 等库发挥作用的地方。
虽然T.J克劳德已经回答了这个问题,但我想我可能会补充一些难以挤入的观点作为评论。
一旦我深入了解我的 Firebase V9 转换,我开始发现“模块命名空间”问题的一些后果是相当深远的。如上所述,我最初的问题中引用的示例很容易处理,但我发现我还需要弄清楚如何处理“动态”HTML 以响应来自数据库的可变情况。在这种情况下,我的 javascript 最初会创建一个包含 HTML 块的字符串,例如:
realDiv = `
<div>
<button onclick = "function fn1 (param1a, param1b,...);">button1</button>
<button onclick = "function fn2 (param2a, param2b,...);">button2</button>
etc
</div>
`
然后将其放入应用程序 HTML 骨架中定义的“真正的” realdiv 中,并带有
document.getElementById("realdiv") = realDiv;
现在,由于上述原因,一旦 javascript 在模块中,这种安排就不再有效了。
我学会采用的模式(再次感谢 T.J Crowder)遵循以下原则:
realDiv = `
<div>
<button id = "button1" data-param1a="param1a" data-param1b="param1b";">button1</button>
<button id = "button2" data-param2a="param2a" data-param2b="param2b";">button2</button>
etc
</div>
`
然后我会像以前一样将生成的代码放入我的 HTML 框架中
document.getElementById("realdiv") = readlDiv;
现在您已经将代码嵌入到 DOM 中(假设我已经统计了您生成的按钮数量),我会为它们创建绑定javascript 的最后一点像这样:
for (let i = 0; i>buttonCount; i++) {
document.getElementById('button' + i).onclick = function() { fn1 (this.getAttribute('data-param1a'), this.getAttribute('data-param1b') };
etc
}
我发现当我需要让 onclick 启动许多不同的功能时,使用这种模式创建 onclicks 对于保持清晰度特别有帮助。