是的,涉及到 JavaScript。有两个部分:为投票“按钮”上的点击事件连接一个处理程序,并将数据发送到服务器。
连接事件在其他地方已经很好地介绍了,我不会在这里讨论它。 (例如,我在this answer 中介绍了它。)
发送数据到服务器,可以使用ajax。在任何尚未完全过时的浏览器上,您都可以使用XMLHttpRequest:
var xhr = new XMLHttpRequest();
xhr.open("POST", "/path/to/server/destination");
xhr.onreadystatechange = handleStateChange;
xhr.send("id=" + encodeURIComponent(id) +
"&user=" + encodeURIComponent(userId) +
"&vote=up");
function handleStateChange() {
if (xhr.readyState === 4) {
// POST complete
if (xhr.status === 200) {
// POST complete and we have response, check it
if (xhr.responseText !== "ok") { // Or whatever you want it to be
// Report problem
}
} else {
// Ajax failed, handle/report problem
}
}
}
在更新的浏览器上,您可以使用fetch:
var body = new FormData();
body.append("id", id);
body.append("user", userId);
body.append("vote", "up");
fetch("/path/to/server/destination", {
method: "POST",
body: body
})
.then(function(res) {
if (!res.ok) {
throw new Error("HTTP error " + res.status);
}
return res.text(); // or `res.json()` if you return JSON
})
.then(function(data) {
if (data !== "ok") { // Or whatever
// Report problem
}
})
.catch(function(error) {
// Ajax failed, handle/report problem
});
只是为了好玩,这里有一个完整的例子:
HTML:
<div class="article" data-itemid="427">
<a href="voteup" class="vote up" >Up</a>
<a href="votedown" class="vote down">Down</a>
<!-- ...the contents of the item... -->
</div>
JavaScript:
document.addEventListener("click", function(event) {
// Regardless of the below, we handle the event, so "consume" it
event.stopPropagation();
event.preventDefault();
// Get the anchor element
var voteLink = event.target.closest("a.vote");
if (!voteLink) {
// Didn't find one, bail
return;
}
// See if the vote has already been done or is in progress
if (voteLink.classList.contains("done") || voteLink.classList.contains("inprogress")) {
// Ignore the click, possibly tell the user why
return;
}
// Get the vote type
var voteType = voteLink.classList.contains("up") ? "up" : "down";
// Get the item we"re voting on
var item = voteLink.closest(".article");
// Get its ID
var itemId = item.getAttribute("data-itemid");
// If we didn"t get an ID...
if (!itemId) {
// ...report error
return;
}
// Mark "in progress" and initiate the vote; action continues
// in our callbacks below
voteLink.classList.add("inprogress");
var body = new FormData();
body.append("itemId", itemId);
body.append("voteType", voteType);
fetch("savevote", {
method: "POST",
body: body
})
.then(function(res) {
if (!res.ok) {
throw new Error("HTTP error " + res.status);
}
return res.text(); // or `res.json()` if you return JSON
})
.then(function(data) {
if (data === "ok") { // Or whatever
voteLink.classList.add("done");
} else {
// Report an error to the user, the server couldn"t record the vote
}
})
.catch(function(error) {
// Ajax failed, handle/report problem
})
.finally(function() {
// Not in progress anymore
voteLink.classList.remove("inprogress");
});
});
一些注意事项:
- 上面的代码是用 ES5 编写的,但您可以在大多数现代浏览器中使用 ES2015+ 功能(或使用 Babel 等工具进行转译)。
- 我在链接上放了一个
href(StackOverflow 没有),这样如果 JavaScript 被禁用,我们可以退回到让用户使用表单提交或投票的页面某物。此外,带有href 的链接会被浏览器(标签目标等)特殊处理,因此这对于可访问性很有用。 (要真正做到这一点,我可能还必须将文章 ID 放在 href 中。)
- 我将我们要投票的项目的 ID 存储在
data- attribute 中。
- 我们通过将“最接近”的文章定位到被点击的按钮来找到要投票的项目。 DOM 的
closest 函数从元素开始并检查该元素以查看它是否适合给定的 CSS 选择器,如果不适合则查看其父级,然后查看 其 父级,等等,直到找到匹配。所以投票按钮通过包含与文章相关联;被投票的文章包含投票按钮。
- 如果您将事件处理程序植根于页面更深处(而不是文档级别)的元素中,您可能会遵循
closest 检查和contains 检查以确保附加处理程序的元素to 包含找到的元素(如果它是在祖先元素中找到的)。那将是上面的!voteLink || !this.contains(voteLink)(而不仅仅是!voteLink)。
- 我使用 POST 因为调用改变了服务器状态,所以 GET 不合适