在這裡記錄一下,自己實際使用時發生的問題,原本使用網路找到的使用 Livewire 來達成欄位自動完成的功能,使用上並不怎麼理想﹐無法使用鍵盤來選擇,後面還是改用 JS 的方式來完成功能,我使用現成W3Cschools 已寫好的 Autocomplete 的 JS,至少是一個可以完整的操作,需要修改自動完成資料取得改成 Laravel 資料庫所產生的 JSON 網址,這樣跳出的自動完成選項就可以維護,下面 W3Cschools 的 JS 程式碼:
JavaScript
function autocomplete(inp, arr) {
/*自動完成函數接受兩個參數,
文字欄位元素和可能的自動完成值的陣列:*/
var currentFocus;
/*當有人在文字欄位中輸入時執行一個函數:*/
inp.addEventListener("input", function(e) {
var a, b, i, val = this.value;
/*關閉任何已經打開的自動完成值列表*/
closeAllLists();
if (!val) { return false;}
currentFocus = -1;
/*創建一個DIV元素,用於包含項目(值):*/
a = document.createElement("DIV");
a.setAttribute("id", this.id + "autocomplete-list");
a.setAttribute("class", "autocomplete-items");
/*將DIV元素作為自動完成容器的子節點追加:*/
this.parentNode.appendChild(a);
/*遍歷陣列中的每個項目...*/
for (i = 0; i < arr.length; i++) {
/*檢查項目是否以文字欄位值相同的字母開頭:*/
if (arr[i].substr(0, val.length).toUpperCase() == val.toUpperCase()) {
/*為每個匹配的元素創建一個DIV元素:*/
b = document.createElement("DIV");
/*將匹配的字母設為粗體:*/
b.innerHTML = "<strong>" + arr[i].substr(0, val.length) + "</strong>";
b.innerHTML += arr[i].substr(val.length);
/*插入一個隱藏的輸入欄位,用於保存當前陣列項目的值:*/
b.innerHTML += "<input type='hidden' value='" + arr[i] + "'>";
/*當有人點擊項目值(DIV元素)時執行一個函數:*/
b.addEventListener("click", function(e) {
/*將值插入到自動完成文字欄位中:*/
inp.value = this.getElementsByTagName("input")[0].value;
/*關閉自動完成值的列表,
(或任何其他打開的自動完成值列表):*/
closeAllLists();
});
a.appendChild(b);
}
}
});
/*當有人在鍵盤上按下某個鍵時執行一個函數:*/
inp.addEventListener("keydown", function(e) {
var x = document.getElementById(this.id + "autocomplete-list");
if (x) x = x.getElementsByTagName("div");
if (e.keyCode == 40) {
/*如果按下的是向下箭頭鍵,
增加currentFocus變數:*/
currentFocus++;
/*並使當前項目更顯眼:*/
addActive(x);
} else if (e.keyCode == 38) { //向上
/*如果按下的是向上箭頭鍵,
減少currentFocus變數:*/
currentFocus--;
/*並使當前項目更顯眼:*/
addActive(x);
} else if (e.keyCode == 13) {
/*如果按下的是ENTER鍵,防止表單提交,*/
e.preventDefault();
if (currentFocus > -1) {
/*並模擬點擊“活動”項目:*/
if (x) x[currentFocus].click();
}
}
});
function addActive(x) {
/*一個用於將項目分類為“活動”的函數:*/
if (!x) return false;
/*首先移除所有項目的“活動”類:*/
removeActive(x);
if (currentFocus >= x.length) currentFocus = 0;
if (currentFocus < 0) currentFocus = (x.length - 1);
/*添加“autocomplete-active”類:*/
x[currentFocus].classList.add("autocomplete-active");
}
function removeActive(x) {
/*一個用於從所有自動完成項目中移除“活動”類的函數:*/
for (var i = 0; i < x.length; i++) {
x[i].classList.remove("autocomplete-active");
}
}
function closeAllLists(elmnt) {
/*關閉文件中所有的自動完成列表,
除了作為參數傳入的那個:*/
var x = document.getElementsByClassName("autocomplete-items");
for (var i = 0; i < x.length; i++) {
if (elmnt != x[i] && elmnt != inp) {
x[i].parentNode.removeChild(x[i]);
}
}
}
/*當有人在文件中點擊時執行一個函數:*/
document.addEventListener("click", function (e) {
closeAllLists(e.target);
});
}
/*包含世界上所有國家名稱的陣列(這裡取得下拉資料刪了一些不燃占版面):*/
var countries = ["Afghanistan","Albania","Algeria","Andorra","Angola","Anguilla","Antigua & Barbuda","Argentina","Armenia","Aruba","Australia","Austria","Azerbaijan","Bahamas","Bahrain","Bangladesh","Barbados" ];
/*在“myInput”元素上啟動自動完成函數,並將國家陣列作為可能的自動完成值傳遞:*/
autocomplete(document.getElementById("myInput"), countries);
//打發時間 dafatime.idv.tw
將下拉選項資料改成從網址取得 JSON 資料
JavaScript
function autocomplete(inp, arr) {
/* 自動完成函數接受兩個參數,
欄位字段元素和可能的自動完成值的數組:*/
var currentFocus;
/* 當有人在欄位字段中輸入時執行一個函數:*/
inp.addEventListener("input", function(e) {
var a, b, i, val = this.value;
/* 關閉任何已經打開的自動完成值列表 */
closeAllLists();
if (!val) { return false;}
currentFocus = -1;
/* 創建一個DIV元素,將包含項目(值):*/
a = document.createElement("DIV");
a.setAttribute("id", this.id + "autocomplete-list");
a.setAttribute("class", "autocomplete-items");
/* 將DIV元素作為自動完成容器的子元素附加:*/
this.parentNode.appendChild(a);
/* 對數組中的每個項目... */
for (i = 0; i < arr.length; i++) {
/* 檢查項目是否以欄位字段值的相同字母開頭:*/
if (arr[i].substr(0, val.length).toUpperCase() == val.toUpperCase()) {
/* 為每個匹配元素創建一個DIV元素:*/
b = document.createElement("DIV");
/* 使匹配的字母加粗:*/
b.innerHTML = "<strong>" + arr[i].substr(0, val.length) + "</strong>";
b.innerHTML += arr[i].substr(val.length);
/* 插入一個輸入字段,將保存當前數組項目的值:*/
b.innerHTML += "<input type='hidden' value='" + arr[i] + "'>";
/* 當有人點擊項目值(DIV元素)時執行一個函數:*/
b.addEventListener("click", function(e) {
/* 為自動完成欄位字段插入值:*/
inp.value = this.getElementsByTagName("input")[0].value;
/* 關閉自動完成值列表,
(或任何其他打開的自動完成值列表:*/
closeAllLists();
});
a.appendChild(b);
}
}
});
/* 當鍵盤上按下一個鍵時執行一個函數:*/
inp.addEventListener("keydown", function(e) {
var x = document.getElementById(this.id + "autocomplete-list");
if (x) x = x.getElementsByTagName("div");
if (e.keyCode == 40) {
/* 如果按下向下箭頭鍵,
增加currentFocus變量:*/
currentFocus++;
/* 並使當前項目更加明顯:*/
addActive(x);
} else if (e.keyCode == 38) { //向上
/* 如果按下向上箭頭鍵,
減少currentFocus變量:*/
currentFocus--;
/* 並使當前項目更加明顯:*/
addActive(x);
} else if (e.keyCode == 13) {
/* 如果按下ENTER鍵,防止表單被提交,*/
e.preventDefault();
if (currentFocus > -1) {
/* 並模擬點擊"活動"項目:*/
if (x) x[currentFocus].click();
}
}
});
function addActive(x) {
/* 一個將項目分類為"活動"的函數:*/
if (!x) return false;
/* 首先移除所有項目上的"活動"類:*/
removeActive(x);
if (currentFocus >= x.length) currentFocus = 0;
if (currentFocus < 0) currentFocus = (x.length - 1);
/* 添加類"autocomplete-active":*/
x[currentFocus].classList.add("autocomplete-active");
}
function removeActive(x) {
/* 一個從所有自動完成項目中移除"活動"類的函數:*/
for (var i = 0; i < x.length; i++) {
x[i].classList.remove("autocomplete-active");
}
}
function closeAllLists(elmnt) {
/* 關閉欄位中的所有自動完成列表,
除了作為參數傳遞的那個:*/
var x = document.getElementsByClassName("autocomplete-items");
for (var i = 0; i < x.length; i++) {
if (elmnt != x[i] && elmnt != inp) {
x[i].parentNode.removeChild(x[i]);
}
}
}
/* 當有人在欄位中點擊時執行一個函數:*/
document.addEventListener("click", function (e) {
closeAllLists(e.target);
});
}
/*包含世界上所有國家名稱的陣列:*/
fetch('http://127.0.0.1:8000/json/cpname-auto')
.then(response => {
if (!response.ok) {
throw new Error('Network response was not ok');
}
return response.json();
})
.then(data => {
var cpname = data;
// 在「myInput」元素上啟動自動完成功能,並傳遞國家數組作為可能的自動完成值:
autocomplete(document.getElementById("cpname"), cpname);
})
.catch(error => console.error('Error fetching company names:', error));
//打發時間 dafatime.idv.tw
在 Livewire 元件內 JS 必需通知 Livewire 自動完成欄位內容已變更
原本以為這樣可以完事,但後面實際操作欄位自動完成動作,按下儲存後,發現表單送出的資料,並未將自動完成選擇的選項完整字串送出,只會送出用鍵盤輸入的字串,後面發現由於 Livewire 並未知道自動完稱欄位已被 JS 變更,所以才會出現無法將選取的字串整送出儲存,修正方法如下面34行程式碼添加通知,這樣表單就可以正常的送出完整字串。
JavaScript
function autocomplete(inp, arr) {
/* 自動完成函數接受兩個參數,
欄位字段元素和可能的自動完成值的數組:*/
var currentFocus;
/* 當有人在欄位字段中輸入時執行一個函數:*/
inp.addEventListener("input", function(e) {
var a, b, i, val = this.value;
/* 關閉任何已經打開的自動完成值列表 */
closeAllLists();
if (!val) { return false;}
currentFocus = -1;
/* 創建一個DIV元素,將包含項目(值):*/
a = document.createElement("DIV");
a.setAttribute("id", this.id + "autocomplete-list");
a.setAttribute("class", "autocomplete-items");
/* 將DIV元素作為自動完成容器的子元素附加:*/
this.parentNode.appendChild(a);
/* 對數組中的每個項目... */
for (i = 0; i < arr.length; i++) {
/* 檢查項目是否以欄位字段值的相同字母開頭:*/
if (arr[i].substr(0, val.length).toUpperCase() == val.toUpperCase()) {
/* 為每個匹配元素創建一個DIV元素:*/
b = document.createElement("DIV");
/* 使匹配的字母加粗:*/
b.innerHTML = "<strong>" + arr[i].substr(0, val.length) + "</strong>";
b.innerHTML += arr[i].substr(val.length);
/* 插入一個輸入字段,將保存當前數組項目的值:*/
b.innerHTML += "<input type='hidden' value='" + arr[i] + "'>";
/* 當有人點擊項目值(DIV元素)時執行一個函數:*/
b.addEventListener("click", function(e) {
/* 為自動完成欄位字段插入值:*/
inp.value = this.getElementsByTagName("input")[0].value;
/* 觸發 input 事件,讓 Livewire 知道值已經改變 */
inp.dispatchEvent(new Event('input', { bubbles: true }));
/* 關閉自動完成值列表,
(或任何其他打開的自動完成值列表:*/
closeAllLists();
});
a.appendChild(b);
}
}
});
/* 當鍵盤上按下一個鍵時執行一個函數:*/
inp.addEventListener("keydown", function(e) {
var x = document.getElementById(this.id + "autocomplete-list");
if (x) x = x.getElementsByTagName("div");
if (e.keyCode == 40) {
/* 如果按下向下箭頭鍵,
增加currentFocus變量:*/
currentFocus++;
/* 並使當前項目更加明顯:*/
addActive(x);
} else if (e.keyCode == 38) { //向上
/* 如果按下向上箭頭鍵,
減少currentFocus變量:*/
currentFocus--;
/* 並使當前項目更加明顯:*/
addActive(x);
} else if (e.keyCode == 13) {
/* 如果按下ENTER鍵,防止表單被提交,*/
e.preventDefault();
if (currentFocus > -1) {
/* 並模擬點擊"活動"項目:*/
if (x) x[currentFocus].click();
/* 不需要在這裡再次觸發 input 事件,因為 click 事件處理程序已經觸發了 */
}
}
});
function addActive(x) {
/* 一個將項目分類為"活動"的函數:*/
if (!x) return false;
/* 首先移除所有項目上的"活動"類:*/
removeActive(x);
if (currentFocus >= x.length) currentFocus = 0;
if (currentFocus < 0) currentFocus = (x.length - 1);
/* 添加類"autocomplete-active":*/
x[currentFocus].classList.add("autocomplete-active");
}
function removeActive(x) {
/* 一個從所有自動完成項目中移除"活動"類的函數:*/
for (var i = 0; i < x.length; i++) {
x[i].classList.remove("autocomplete-active");
}
}
function closeAllLists(elmnt) {
/* 關閉欄位中的所有自動完成列表,
除了作為參數傳遞的那個:*/
var x = document.getElementsByClassName("autocomplete-items");
for (var i = 0; i < x.length; i++) {
if (elmnt != x[i] && elmnt != inp) {
x[i].parentNode.removeChild(x[i]);
}
}
}
/* 當有人在欄位中點擊時執行一個函數:*/
document.addEventListener("click", function (e) {
closeAllLists(e.target);
});
}
/*包含世界上所有國家名稱的陣列:*/
fetch('http://127.0.0.1:8000/json/cpname-auto')
.then(response => {
if (!response.ok) {
throw new Error('Network response was not ok');
}
return response.json();
})
.then(data => {
var cpname = data;
// 在「myInput」元素上啟動自動完成功能,並傳遞國家數組作為可能的自動完成值:
autocomplete(document.getElementById("cpname"), cpname);
})
.catch(error => console.error('Error fetching company names:', error));
//打發時間 dafatime.idv.tw
最後
上面是我在使用 Livewire 將表單寫在元件時所發生的小插曲,而將表單寫在 Laravel 視圖(Blade) 上述 W3Cschools 的自動完成 JS 就沒什麼問題不需要修改,那就這樣啦!感謝觀賞,掰掰~!