这个应用程序是一个非常基本的数据库应用程序的前端。前端假设后端数据库将具有与html表单相同的字段。
我对db2form.js的一些关注是:
至于css文件,可能会有很大的改进。
对此申请有任何反馈将是非常欢迎的。
html页面:
Itel Office
Contacts
Call Log
Contacts
Enter text below and click Search button to find a contact
ID:
Name:
Company:
Email:
Telephone:
Mobile:
alt Telephone:
Web:
Address line 1:
Address line 2:
Address line 3:
Address line 4:
Postcode:
Category:
Notes:
Search
New
Edit
Save
Delete
First
Next
Prior
Lastcss文件style.css:
body{
background-color: #ffff00;
}
nav{
box-sizing:border-box;
background-color:#409fff; /* blue we like */
display: inline-block;
width: 20%;
min-width: 125px;
margin-right:15px;
height:100vh;
overflow: auto;
}
nav a{
display:block;
line-height: 45px;
height:45px;
color: #FFFFFF;
text-decoration: none;
padding-left: 50px;
margin:10px 0 10px 5px;
}
section{
display: inline-block;
width:70%;
height:100vh;
overflow: auto;
}
h1{
color: #409fff;
padding: 2px;
margin: 0;
}
form {
display: grid;
grid-template-columns: 150px 1fr;
border: 0;
}
label {
grid-column: 1 / 2;
margin: 0;
padding:0;
border: 0;
}
input{
grid-column: 2 / 3;
margin: 0;
padding:0;
border: 0;
border-radius: 5px;
}
/*input:focus{
background-color: #fcfab1;
}
*/
textarea{
border-radius: 5px;
height: 20px;
}
.buttons{
display: grid;
grid-column: 2 / 3;
grid-gap: 10px;
grid-template-columns: 1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr;
}javascript文件db2form.js:
let current_contact_idx = -1;
let records = null;
function search_mode() {
// now change button to say Search
document.forms.searchform.elements.search.innerText = "Search";
document.forms.searchform.elements.new.disabled = false;
document.forms.searchform.elements.edit.disabled = true;
document.forms.searchform.elements.save.disabled = true;
document.forms.searchform.elements.delete.disabled = true;
document.forms.searchform.elements.first.disabled = true;
document.forms.searchform.elements.next.disabled = true;
document.forms.searchform.elements.prior.disabled = true;
document.forms.searchform.elements.last.disabled = true;
}
function found_mode() {
// now change button to say Cancel
document.forms.searchform.elements.search.innerText = "Cancel";
document.forms.searchform.elements.new.disabled = false;
document.forms.searchform.elements.edit.disabled = false;
document.forms.searchform.elements.save.disabled = true;
document.forms.searchform.elements.delete.disabled = false;
document.forms.searchform.elements.first.disabled = false;
document.forms.searchform.elements.next.disabled = false;
document.forms.searchform.elements.prior.disabled = false;
document.forms.searchform.elements.last.disabled = false;
}
function new_edit_mode() {
// now change button to say Cancel
document.forms.searchform.elements.search.innerText = "Cancel";
document.forms.searchform.elements.new.disabled = true;
document.forms.searchform.elements.edit.disabled = true;
document.forms.searchform.elements.save.disabled = false;
document.forms.searchform.elements.delete.disabled = true;
document.forms.searchform.elements.first.disabled = true;
document.forms.searchform.elements.next.disabled = true;
document.forms.searchform.elements.prior.disabled = true;
document.forms.searchform.elements.last.disabled = true;
}
function server_response_callback_search(ajax) {
let form_elements = document.forms.searchform.elements;
if(ajax.responseText.length == 0) {
cancel_step(form_elements);
document.getElementById('status').innerHTML = "No record found for your search."
return;
}
console.log("server_response_callback_search response type: " + ajax.getResponseHeader('content-type'));
records = JSON.parse(ajax.responseText);
if (records.contacts.length > 0) {
current_contact_idx = 0;
populate_field(records.contacts[current_contact_idx]);
found_mode();
} else {
current_contact_idx = -1; // reset to no record found
search_mode(); // stay in search mode
}
// display message
if (current_contact_idx == -1) {
document.getElementById('status').innerHTML = "No record found which matches the criteria";
} else {
document.getElementById('status').innerHTML = "Displaying record " + (current_contact_idx + 1).toString() + " of " + records.contacts.length;
}
}
function server_response_callback_update(ajax, rowid) {
console.log("server_response_callback_update response type: " + ajax.getResponseHeader('content-type'));
let form_elements = document.forms.searchform.elements;
search_mode();
// empty all input and textarea fields
for (let element of form_elements) {
if(element.type != 'hidden') {
element.value = "";
}
}
document.getElementById('status').innerHTML = ajax.responseText;;
}
function server_response_callback_insert(ajax) {
console.log("server_response_callback_insert response type: " + ajax.getResponseHeader('content-type'));
let form_elements = document.forms.searchform.elements;
search_mode();
// empty all input and textarea fields
for (let element of form_elements) {
if(element.type != 'hidden') {
element.value = "";
}
}
document.getElementById('status').innerHTML = ajax.responseText;
}
// We need to display what it is that database.exe returns for these cases
function server_response_callback_delete(ajax, rowid) {
console.log("server_response_callback_delete response type: " + ajax.getResponseHeader('content-type'));
let form_elements = document.forms.searchform.elements;
search_mode();
// empty all input and textarea fields
for (let element of form_elements) {
if(element.type != 'hidden') {
element.value = "";
}
}
document.getElementById('status').innerHTML = ajax.responseText;
}
function populate_field(element) {
let formelements = document.forms.searchform.elements;
// formelements is an array
for (let i = 0; i < formelements.length; i++) {
if (formelements[i].name in element) {
formelements[i].value = element[formelements[i].name];
} else {
formelements[i].value = "";
}
}
document.getElementById('status').innerHTML = "Displaying record " + (current_contact_idx + 1).toString() + " of " + records.contacts.length;
}
function edit_step() {
new_edit_mode();
}
function cancel_step(form_elements) {
search_mode();
// empty all input and textarea fields
for (let element of form_elements) {
if(element.type != 'hidden') {
element.value = "";
}
}
document.getElementById('status').innerHTML = "";
}
function new_step(form_elements) {
new_edit_mode();
// empty all input and textarea fields
for (let element of form_elements) {
if(element.type != 'hidden') {
element.value = "";
}
}
document.getElementById('status').innerHTML = "Enter data for new contact, then click Save button to save to database";
}
function extract_form_values(form_elements) {
let query = "";
let first = "yes";
for (let element of form_elements) {
if(["text", "textarea", "tel", "email"].includes(element.type)) {
if(first == "no") {
query += "&";
}
first = "no";
query += element.name;
query += "=";
query += element.value;
}
}
return query;
}
function save_step(form_elements) {
let request_payload = extract_form_values(form_elements);
if(request_payload.length == 0) {
//alert("You need to enter some data to save to database");
document.getElementById('status').innerHTML = "You need to enter some data to save to database";
return;
}
// we determine whether to UPDATE or INSERT based on presence of rowid.
// if a rowid assume updating an existing contact, otherwise a new contact
if (document.forms.searchform.elements.rowid.value == "") {
// go down INSERT route
// remove rowid= from payload
let pos = request_payload.indexOf("rowid=&");
if (pos != -1) {
// remove string
request_payload = request_payload.replace("rowid=&", "");
}
request_payload += "&operation=INSERT";
console.log("sending query to database server: " + request_payload);
// setup ajax callback to handle response
ajax_post("/cgi-bin/database.exe", request_payload, server_response_callback_insert);
} else {
let rowid = parseInt(document.forms.searchform.elements.rowid.value, 10);
request_payload += "&operation=UPDATE";
console.log("sending query to database server: " + request_payload);
// setup ajax callback to handle response
ajax_post("/cgi-bin/database.exe", request_payload, server_response_callback_update, rowid);
}
}
function has_values(form_elements) {
for (let element of form_elements) {
if(["text", "textarea", "tel", "email"].includes(element.type) && element.name != "rowid" && element.value != "") {
return true;
}
}
return false;
}
function insert_step(form_elements) {
// check user actually entered some data in fields
if(!has_values(form_elements)) {
console.log("attempting to insert but no values populated");
document.getElementById('status').innerHTML = "Enter contact details to add a new contact";
return;
}
let request_payload = extract_form_values(form_elements);
if(request_payload.length == 0) {
document.getElementById('status').innerHTML = "You need to enter some update a contact";
return;
}
request_payload += "&operation=INSERT";
console.log("sending query to database server: " + request_payload);
// setup ajax callback to handle response
ajax_post("/cgi-bin/database.exe", request_payload, server_response_callback_insert);
}
function search_step(form_elements) {
let query = extract_form_values(form_elements);
query += query.length == 0 ? "operation=SELECT" : "&operation=SELECT";
console.log("sending query to database server: " + query);
// setup ajax callback to handle response
ajax_post("/cgi-bin/database.exe", query, server_response_callback_search);
}
function ajax_post(url, request, callback, arg) {
// setup ajax callback to handle response
var xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) {
callback(this, arg);
}
};
xhttp.open("POST", url, true);
xhttp.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
xhttp.send(request);
}
function delete_step(form_elements) {
if(form_elements.rowid.value == "") {
const delete_msg = "Form not in correct state to delete a contact";
document.getElementById('status').innerHTML = delete_msg;
alert(delete_msg);
return;
}
let rowid = parseInt(form_elements.rowid.value, 10);
// DELETE FROM table_name WHERE condition;
let request = `rowid=${rowid}&operation=DELETE`;
console.log("sending request to database server: " + request);
let confirmation = confirm("Click Ok if you are absolutely sure you want to delete this contact from the database");
if (confirmation) {
// setup ajax callback to handle response
ajax_post("/cgi-bin/database.exe", request, server_response_callback_delete, rowid);
}
}
function process(buttontext) {
console.log(`buttontext=${buttontext}`);
let form_elements = document.forms.searchform.elements;
if (buttontext == "New") {
new_step(form_elements);
}else if (buttontext == "Edit") {
edit_step();
} else if (buttontext == "Save") {
save_step(form_elements);
} else if (buttontext == "Search") {
search_step(form_elements);
} else if (buttontext == "Cancel") {
cancel_step(form_elements);
} else if (buttontext == "Delete") {
delete_step(form_elements);
} else if (buttontext == "First") {
if (records.contacts.length != 0) {
current_contact_idx = 0;
populate_field(records.contacts[current_contact_idx]);
}
} else if (buttontext == "Next") {
if (records.contacts.length > (current_contact_idx + 1)) {
populate_field(records.contacts[++current_contact_idx]);
} else {
document.getElementById('status').innerHTML = "You are on the last record";
}
} else if (buttontext == "Prior") {
if (current_contact_idx > 0) {
populate_field(records.contacts[--current_contact_idx]);
} else {
document.getElementById('status').innerHTML = "You are on the first record";
}
} else if (buttontext == "Last") {
if (records.contacts.length != 0) {
current_contact_idx = records.contacts.length - 1;
populate_field(records.contacts[current_contact_idx]);
}
} else {
document.getElementById('status').innerHTML = "something has gone wrong - button text incorrectly set";
}
}
// user can press Enter key to invoke search, Esc key to cancel (go back to ready to search mode)
document.onkeydown = function(evt) {
evt = evt || window.event;
var isEscape = false;
var isEnter = false;
if ("key" in evt) {
isEscape = (evt.key === "Escape" || evt.key === "Esc");
isEnter = (evt.key === "Enter");
} else {
isEscape = (evt.keyCode === 27);
isEnter = (evt.keyCode === 13);
}
if (isEscape) {
// only handle Escape if Cancel button enabled
if(document.forms.searchform.elements.search.innerText == "Cancel") {
process("Cancel");
}
} else if (isEnter) {
// only handle Enter if Search button enabled
if(document.forms.searchform.elements.search.innerText == "Search") {
process("Search");
}
}
};发布于 2020-04-27 01:12:12
在Javascript方面:
不要使用内联处理程序,它们有值得使用的太多的问题了。相反,使用Javascript和addEventListener附加侦听器。
由于每次单击包含Search的按钮,您都希望将按钮的文本内容传递给process,因此可以通过检查处理程序中单击按钮的textContent来简洁地完成此操作。
通常最好使用querySelector (它接受简洁、灵活的CSS字符串)来选择元素,而不是通过document.forms:
document.querySelector('.buttons').addEventListener('click', ({ target }) => {
if (!target.matches('button')) return;
process(target.textContent);
});使用上述代码将允许您从.buttons > button元素(包括onclick="process(document.forms.searchform.elements.search.innerText)" )中删除所有内联处理程序。
const process = console.log;
document.querySelector('.buttons').addEventListener('click', ({ target }) => {
if (!target.matches('button')) return;
process(target.textContent);
}); Search
New
Edit
Save
Delete
First
Next
Prior
Last最好使用textContent,这是从元素中提取文本的标准方法,而不是innerText,这是Internet中具有相当奇怪的行为的一种特殊属性。innerText几乎从来都不是你想要的。
与其在search_mode、found_mode、new_edit_mode中一次又一次地选择按钮,不如考虑选择它们一次,然后构造一个按元素类型索引的对象:
const buttons = {};
for (const button of document.querySelectorAll('.buttons > button')) {
buttons[button.textContent.toLowerCase()] = button;
}
function enableDisableButtons(newVal) {
for (const button of buttons) {
button.disabled = newVal;
}
}
function search_mode() {
buttons.search.textContent = 'Search';
enableDisableButtons(true);
buttons.new.disabled = false;
}
function found_mode() {
buttons.search.textContent = 'Cancel';
enableDisableButtons(false);
buttons.save.disabled = true;
}
function new_edit_mode() {
buttons.search.textContent = 'Cancel';
enableDisableButtons(true);
buttons.save.disabled = false;
}您还可以保存对status元素的引用,而不是经常重新选择它。
const status = document.querySelector('#status');
// ...
status.innerHTML = "Displaying record " + (current_contact_idx + 1).toString() + " of " + records.contacts.length;上面的代码还指出了另一个问题--除非您有意插入HTML,否则应该通过分配给textContent而不是innerHTML来设置元素的文本内容。如果代码不可信,使用innerHTML可能会导致任意代码的执行,此外,它比textContent慢,而且对脚本阅读器来说更令人困惑。所以,对于上面的内容,你应该选择
status.textContent = "Displaying record " + (current_contact_idx + 1).toString() + " of " + records.contacts.length;在您的process函数中,而不是对参数进行大量的if/else检查,您可以考虑使用按钮文本索引对象,当需要处理该按钮时,它的值就是您想要运行的函数。在处理程序中,只需查找对象上的函数并运行它:
const actionsByButtonText = {
New: new_step,
Edit: edit_step,
Save: save_step,
// ...
};
function process(buttontext) {
console.log(`buttontext=${buttontext}`);
const fn = actionsByButtonText[buttontext];
if (fn) fn();
else status.textContent = "something has gone wrong - button text incorrectly set";
}(不需要将form_elements传递给这些函数--它们可以迭代上面的buttons对象,这作为一个参数没有多大意义,因为它永远不会改变)
在声明变量时,默认情况下您使用的是let。对总用const最好--除非您必须重新分配,否则不要使用let,并且永远不要使用var (就像在您的ajax_post中)。使用const向脚本的后续读者(包括您)指出,变量名称永远不会重新分配,这比允许使用let重新分配要少一些认知开销。
在Javascript中,变量几乎总是使用camelCase命名的,如果您想要遵循它,您可能需要考虑它。
这个脚本有点长--最初是371行。一旦您有了一个具有超过3-4个函数的脚本,我会强烈考虑使用模块来组织它。拥有单独的模块,每个模块都有自己的功能,比拥有一个大文件更易于维护。模块也很有用,因为它们之间的依赖是显式的,而不是所有的东西都是全局的,并且有可能引用其他所有的东西--当代码不是微不足道的时候,这可能会使事情变得有点混乱。看看像webpack这样的东西。
您还应该考虑在HTML中使用适当的缩进,这样可以使结构更加可读性。这个例子:
Contacts
Enter text below and click Search button to find a contact
ID:
很可能是
Contacts
Enter text below and click Search button to find a contact
ID:
...还有其他的改进也可以做,但这应该是一个良好的开端。https://codereview.stackexchange.com/questions/241259
复制相似问题