我面临的挑战是使用普通的旧Javascript构建单页应用程序,不允许使用任何库或框架。虽然在React和Angular中创建动态DOM元素相当简单,但我提出的vanilla JS解决方案似乎很笨拙。我想知道是否有一种特别简洁或有效的方法来构建动态呈现的DOM元素?
下面的函数接收一个从GET请求接收的数组,并为每个项目呈现一个div,传入该值(就像你在React和渲染子元素中映射结果一样)。
function loadResults(array) {
array.forEach(videoObject => {
let videoData = videoObject.snippet;
let video = {
title : videoData.title,
img : videoData.thumbnails.default.url,
description : videoData.description
};
let div = document.createElement("DIV");
let img = document.createElement("IMG");
img.src = video.img;
let h4 = document.createElement("h4");
let title = document.createTextNode(video.title);
h4.appendChild(title);
let p = document.createElement("p");
let desc = document.createTextNode(video.description);
p.appendChild(desc);
div.appendChild(img);
div.appendChild(h4);
div.appendChild(p);
document.getElementById('results')
.appendChild(div);
});
}
这感觉不必要地笨重,但我还没有找到一种更简单的方法来做到这一点。
提前致谢!
答案 0 :(得分:10)
注意:我在这里说的一切都是概念证明等级,仅此而已。它不处理错误或例外情况,也不处理 它在生产中进行了测试请自行决定使用。
一个好的方法是创建一个为您创建元素的函数。像这样:
const crEl = (tagName, attributes = {}, text) => {
const el = document.createElement(tagName);
Object.assign(el, attributes);
if (text) { el.appendChild(document.createTextNode(text)); }
return el;
};
然后你可以像这样使用它:
results
.map(item => crEl(div, whateverAttributes, item.text))
.forEach(el => someParentElement.appendChild(el));
我见过的另一个很酷的概念证据是使用 ES6 Proxies as a sort of templating engine 。
const t = new Proxy({}, {
get(target, property, receiver) {
return (children, attrs) => {
const el = document.createElement(property);
for (let attr in attrs) {
el.setAttribute(attr, attrs[attr]);
}
for (let child of(Array.isArray(children) ? children : [children])) {
el.appendChild(typeof child === "string" ? document.createTextNode(child) : child);
}
return el;
}
}
})
const el = t.div([
t.span(
["Hello ", t.b("world!")], {
style: "background: red;"
}
)
])
document.body.appendChild(el);

代理捕获目标对象(为空)上的get
,并呈现一个名为被调用方法的元素。这会导致您在const el =
时看到非常酷的语法。
答案 1 :(得分:3)
如果你可以使用ES6,模板字符串是另一个想法 - >
var vids = [
{
snippet: {
description: 'hello',
title: 'test',
img: '#',
thumbnails: { default: {url: 'http://placehold.it/64x64'} }
}
}
];
function loadResults(array) {
array.forEach(videoObject => {
let videoData = videoObject.snippet;
let video = {
title : videoData.title,
img : videoData.thumbnails.default.url,
description : videoData.description
};
document.getElementById('results').innerHTML = `
<div>
<img src="${video.img}"/>
<h4>${video.title}</h4>
<p>${video.description}</p>
</div>
`;
});
}
loadResults(vids);
&#13;
<div id="results"></div>
&#13;
答案 2 :(得分:2)
在我看来,如果你没有使用任何模板引擎,你需要尽可能多地控制你如何组合你的元素。因此,合理的方法是抽象常见任务并允许链接调用以避免额外的变量。所以我会选择这样的东西(不是很花哨):
function CE(el, target){
let ne = document.createElement(el);
if( target )
target.appendChild(ne);
return ne;
}
function CT(content, target){
let ne = document.createTextNode(content);
target.appendChild(ne);
return ne;
}
function loadResults(array) {
var results = document.getElementById('results');
array.forEach(videoObject => {
let videoData = videoObject.snippet;
let video = {
title : videoData.title,
img : videoData.thumbnails.default.url,
description : videoData.description
};
let div = CE('div');
let img = CE("IMG", div);
img.src = video.img;
CT(video.title, CE("H4", div));
CT(video.description, CE("p", div););
results.appendChild(div);
});
}
您获得的是,您仍然可以很好地控制元素的组合方式,以及与之相关的元素。但是您的代码更容易理解。
答案 3 :(得分:0)
关于这个code review问题的两个观察结果:
按
重构video
对象抽象,因为它以相同的名称复制了三个属性中的两个,生成可读但香草类型的Javascript:
function loadResults(array) {
function create (type,text) {
let element = document.createElement(type);
if( text)
element.appendChild( document.createTextNode( text));
return element;
}
array.forEach(videoObject => {
let vid = videoObject.snippet;
let div = create("DIV");
let img = create("IMG");
img.src = vid.thumbnails.default.url;
let h4 = create("H4", vid.title);
let p = create("P", vid.description);
div.appendChild(img);
div.appendChild(h4);
div.appendChild(p);
document.getElementById('results').appendChild(div);
});
}