![](/img/trans.png)
[英]How to create multiple elements by DOM in vanilla javascript?
[英]Vanilla Javascript: How to use cloned DOM node multiple times?
我正在創建一個非常基本的購物車。
它具有相關的下拉菜單和一個"Add more products"
按鈕,將在同一下拉菜單中再添加一行。
有2個下拉菜單第2個菜單必須保持disabled
狀態,直到在第1個菜單中選擇了一個選項。 在第二個菜單中選擇一個選項之前,必須disabled
數量輸入。 Add more products
已啟用數量被添加
我正在使用cloneNode()
為新行添加代碼。
因為它僅在我每次單擊"Add more products"
按鈕時都會創建克隆創建,所以調用new_products();
我正在使用最后添加的行來創建新的Clone
新行被添加,但是問題是第二菜單,並且該行中的數量輸入已enabled
。
請嘗試使用Vanilla(純)JavaScript提供解決方案。
編輯1:我來了一半。
在追加克隆之前,我嘗試訪問那些元素並更改disabled
屬性值。
在function new_products()
:
var order = document.getElementById('order_now');
var product = document.getElementsByClassName('product');
var clone = product[no_of_products-1].cloneNode(true);
clone.getElementsByClassName('second_select')[0].disabled=true;
clone.getElementsByClassName('add_btn')[0].disabled=true;
但這僅適用於第二個下拉菜單。
它不適用於數量input
控制。
程式碼片段:
var productsByCategory = { A: ["Select sub-product", "CNC 1", "CNC 2", "CNC 3", "CNC 4"], B: ["Select sub-product", "LASER 1", "LASER 2", "LASER 3", "LASER 4"], C: ["Select sub-product", "RUBBER 1", "RUBBER 2", "RUBBER 3", "RUBBER 4", "RUBBER 5"], D: ["Select sub-product", "PRECISION 1", "PRECISION 2", "PRECISION 3"] } var valuesByCategory = { A: ["", "A1", "A2", "A3", "A4"], B: ["", "B1", "B2", "B3", "B4"], C: ["", "C1", "C2", "C3", "C4", "C5"], D: ["", "D1", "D2", "D3"] } var no_of_products = 1; function dropdown() { var select = document.getElementsByClassName('first_select'); var selected = select[no_of_products - 1].value; var target = document.getElementsByClassName('second_select'); var targetLength = target[no_of_products - 1].length /*console.log("Length"+target.length);*/ for (var i = targetLength; i >= 0; i--) { /*console.log(i);*/ target[no_of_products - 1].remove(i); } if (selected == 0) { var option = document.createElement("option"); option.text = "Select Product first"; option.value = ""; target[no_of_products - 1].add(option); target[no_of_products - 1].disabled = true; } if (selected == 1) { for (var i in productsByCategory['A']) { var option = document.createElement("option"); //If this is outside the lopp then only last option gets included. option.text = productsByCategory['A'][i]; option.value = valuesByCategory['A'][i]; target[no_of_products - 1].add(option); target[no_of_products - 1].disabled = false; } } else if (selected == 2) { for (var i in productsByCategory['B']) { var option = document.createElement("option"); option.text = productsByCategory['B'][i]; option.value = valuesByCategory['B'][i]; target[no_of_products - 1].add(option); target[no_of_products - 1].disabled = false; } } else if (selected == 3) { for (var i in productsByCategory['C']) { var option = document.createElement("option"); option.text = productsByCategory['C'][i]; option.value = valuesByCategory['C'][i]; target[no_of_products - 1].add(option); target[no_of_products - 1].disabled = false; } } else { for (var i in productsByCategory['D']) { var option = document.createElement("option"); option.text = productsByCategory['D'][i]; option.value = valuesByCategory['D'][i]; target[no_of_products - 1].add(option); target[no_of_products - 1].disabled = false; } } } function dropdown2() { var select = document.getElementsByClassName('second_select'); var selected = select[no_of_products - 1].value; /*console.log(selected);*/ var submit = document.getElementsByClassName('s_btn'); submit[no_of_products - 1].disabled = false; var add = document.getElementById('add_button'); add.disabled = false; } function new_products() { var order = document.getElementById('order_now'); var product = document.getElementsByClassName('product'); var clone = product[no_of_products - 1].cloneNode(true); clone.getElementsByClassName('second_select')[0].disabled = true; clone.getElementsByClassName('add_btn')[0].disabled = true; var add = document.getElementById('add_button'); product[no_of_products - 1].removeChild(add); /*console.log(clone);*/ order.appendChild(clone); no_of_products += 1; }
body { height: 100vh; margin: 0px; overflow-y: auto; font-family: 'Roboto'; } #clear { clear: both; } .content { display: flex; background-color: white; height: auto; margin-top: 0px; font-family: 'Roboto'; z-index: -1; min-height: 88%; } .link-contents { position: relative; display: block; float: left; left: 0px; width: 100%; } .option-links { display: block; font-size: 30px; cursor: pointer; } #op1 { background-color: #cccccc; } select, button, input { position: relative; top: 5em; display: block; width: 12em; height: 2em; } button { width: 8em; } .first_select { position: relative; float: left; left: 10%; } .second_select { position: relative; float: left; left: 20%; } .s_btn { position: relative; float: left; left: 30%; } .add_btn { float: left; top: 6em; width: 10em; left: 5em; } .footer { display: block; max-height: 4%; } .option-contents { display: none; } #order_now { display: block; }
<!DOCTYPE html> <html> <head> <link href='https://fonts.googleapis.com/css?family=Roboto' rel='stylesheet'> <link rel="stylesheet" type="text/css" href="profile.css"> <title></title> </head> <body> <div class="content"> <div class="link-contents"> <div class="option-contents" id="order_now"> <div class="product"> <select class="first_select" onchange="dropdown();"> <option value="0">Select</option> <option value="1">CNS</option> <option value="2">Laser Cut</option> <option value="3">Rubber roller</option> <option value="4">Fixture</option> </select> <select class="second_select" onchange="dropdown2();" disabled> <option>Select Product first</option> </select> <input class="s_btn" type="number" min='1' value="1" disabled /> <br/> <button class="add_btn" id="add_button" onclick="new_products();" disabled>Add more products</button> <div id="clear"></div> </div> </div> </div> <div id="clear"></div> </div> <div class="footer"> A big thank you to all of you. </div> </body> <script type="text/javascript" src="profile.js"></script> </html>
為了在每次點擊“添加更多產品” button.add_btn
添加div.product
的克隆,您需要創建一個全局變量,該變量保存div.product
的克隆,該克隆在頁面加載時顯示(I意味着div.product
具有select.second_select
的input.s_btn
和button.add_btn
是開始禁用),那么當button.add_btn
被點擊,我們將創建一個新div
元素,給它一個類的product
,然后將該div
附加到div#order_now
,然后我們將使用克隆的元素的innerHTML
屬性並將其分配給新創建的div
。
一些意見和建議
我將在答案的末尾添加一個可運行的代碼段,但在此之前,我想告訴您一些要點:
您正在使用inline event listeners
,這不是明智的選擇,應改用addEventListener
方法。 使用addEventListener
方法,您可以同時附加多個事件(當然,當這些事件共享相同的處理邏輯時),還可以根據需要將任意數量的處理程序附加到同一事件(唯一的問題是客戶端內存)和性能問題)。 addEventListener
方法的另一個重要功能是final參數,該參數控制偵聽器對冒泡事件的反應,使用內聯事件時沒有等效的參數。
為了使動態創建的元素不受事件(例如select
元素上的“ change”事件)的捕獲,我們需要將事件附加到document
並檢查應執行哪個處理程序函數。 換句話說,我們將使用Event Delegqtion
。
在我的回答中,您代碼中的某些變量將不再用作no_of_products
變量,而其他變量(希望您會注意到它們自己)。
我從div.product
元素中刪除了所有冗余ID
的屬性,特別是在button
, input
和select
,因為ID
在頁面中必須是唯一的。 另外,我將刪除所有內聯事件處理程序,因為我們將使用addEventListener
方法。
正如我所說,內聯事件處理程序將由addEventListener
方法代替,並且某些變量不再有用,以檢查應執行哪個處理程序功能,我們將依靠Event
的target
屬性來查看哪個元素是當前事件的目標,因此我們將知道應執行哪個處理函數。
為了讓在我們的代碼(處理函數格外)更多的控制,將使用this
關鍵字來引用當前事件的目標(像這樣,無需no_of_products
變量,因為每個元素將引用使用this
關鍵字時,它是當前事件的目標,甚至將支持動態創建的元素,即:在new_products
函數中, this
是指單擊的button.add_btn
元素)。 為此,我們將在處理程序函數上使用call
方法,該方法允許我們指定處理程序函數中this
關鍵字引用的元素。
話雖如此,下面是一個可運行的代碼片段,用以說明:
var productsByCategory = { A: ["Select sub-product", "CNC 1", "CNC 2", "CNC 3", "CNC 4"], B: ["Select sub-product", "LASER 1", "LASER 2", "LASER 3", "LASER 4"], C: ["Select sub-product", "RUBBER 1", "RUBBER 2", "RUBBER 3", "RUBBER 4", "RUBBER 5"], D: ["Select sub-product", "PRECISION 1", "PRECISION 2", "PRECISION 3"] }, valuesByCategory = { A: ["", "A1", "A2", "A3", "A4"], B: ["", "B1", "B2", "B3", "B4"], C: ["", "C1", "C2", "C3", "C4", "C5"], D: ["", "D1", "D2", "D3"] }, /** * create a clone element using 'querySelector' method * which DOESN'T return a 'live node' (it returns a 'static node'), thus gain performance. **/ clone = document.querySelector('.product').cloneNode(true); /** * add event listeners to the body rather than the specific elements * thus the dynamically created elements are also supported and catchable by the events. * using some checking to get the desired handler function to be called. * using the 'call' method, we specify to which element in the handler functions the 'this' keyword refers to. **/ document.addEventListener('change', function(e) { (e.target instanceof HTMLSelectElement && ((e.target.classList.contains('first_select') && dropdown.call(e.target)) || (e.target.classList.contains('second_select') && dropdown2.call(e.target)))); /** * the above code is the same as the next but it's faster. if (e.target instanceof HTMLSelectElement && e.target.classList.contains('first_select')) { dropdown.call(e.target) } else if (e.target instanceof HTMLSelectElement && e.target.classList.contains('second_select')) { dropdown2.call(e.target) } **/ }); document.addEventListener('click', function(e) { (e.target instanceof HTMLButtonElement && e.target.classList.contains('add_btn') && new_products.call(e.target)); /** * the above code is the same as the next but it's faster. if(e.target instanceof HTMLButtonElement && e.target.classList.contains('add_btn')) { new_products.call(e.target); } **/ }) /** * from now on, the handler function use the 'this' keyword to reference the desired element, even the dynamically created ones are supporyted. * So, 'this' === the argument that passed to the 'call' method. **/ function dropdown() { var selected = this.value; var target = this.parentNode.getElementsByClassName('second_select')[0]; var targetLength = target.length; for (var i = targetLength; i >= 0; i--) { target.remove(i); } if (selected == 0) { var option = document.createElement("option"); option.text = "Select Product first"; option.value = ""; target.add(option); /** * you missed to disable the 'button' and the 'input' if the selected value is '0'. **/ this.parentNode.querySelector('.s_btn').disabled = true; this.parentNode.querySelector('.add_btn').disabled = true; target.disabled = true; } else if (selected == 1) { for (var i in productsByCategory['A']) { var option = document.createElement("option"); option.text = productsByCategory['A'][i]; option.value = valuesByCategory['A'][i]; target.add(option); target.disabled = false; } } else if (selected == 2) { for (var i in productsByCategory['B']) { var option = document.createElement("option"); option.text = productsByCategory['B'][i]; option.value = valuesByCategory['B'][i]; target.add(option); target.disabled = false; } } else if (selected == 3) { for (var i in productsByCategory['C']) { var option = document.createElement("option"); option.text = productsByCategory['C'][i]; option.value = valuesByCategory['C'][i]; target.add(option); target.disabled = false; } } else { for (var i in productsByCategory['D']) { var option = document.createElement("option"); option.text = productsByCategory['D'][i]; option.value = valuesByCategory['D'][i]; target.add(option); target.disabled = false; } } } function dropdown2() { this.parentNode.getElementsByClassName('s_btn')[0].disabled = false; this.parentNode.getElementsByClassName('add_btn')[0].disabled = false; } function new_products() { var order = document.getElementById('order_now'), /** * create a 'div' element which will hold the cloned element's 'innerHTML'. **/ product = document.createElement('div'); /** * give that 'div' element the 'product' class. **/ product.className = 'product'; /** * append that 'div' to the 'div#order_now' element. * it's now the last child of the 'div#order_now' element. **/ order.appendChild(product); /** * assign the cloned element's 'innerHTML' to the newly created 'div' using the 'lastChild' attribute. * with that we eliminate the possibility of directly appending the cloned element to 'div#order_now' to run only once. **/ order.lastChild.innerHTML = clone.innerHTML; this.parentNode.removeChild(this) }
body { height: 100vh; margin: 0px; overflow-y: auto; font-family: 'Roboto'; } #clear { clear: both; } .content { display: flex; background-color: white; height: auto; margin-top: 0px; font-family: 'Roboto'; z-index: -1; min-height: 88%; } .link-contents { position: relative; display: block; float: left; left: 0px; width: 100%; } .option-links { display: block; font-size: 30px; cursor: pointer; } #op1 { background-color: #cccccc; } select, button, input { position: relative; top: 5em; display: block; width: 12em; height: 2em; } button { width: 8em; } .first_select { position: relative; float: left; left: 10%; } .second_select { position: relative; float: left; left: 20%; } .s_btn { position: relative; float: left; left: 30%; } .add_btn { float: left; top: 6em; width: 10em; left: 5em; } .footer { display: block; max-height: 4%; } .option-contents { display: none; } #order_now { display: block; } select, input { display: block !important; }
<div class="content"> <div class="link-contents"> <div class="option-contents" id="order_now"> <div class="product"> <select class="first_select"> <option value="0">Select</option> <option value="1">CNS</option> <option value="2">Laser Cut</option> <option value="3">Rubber roller</option> <option value="4">Fixture</option> </select> <select class="second_select" disabled> <option>Select Product first</option> </select> <input class="s_btn" type="number" min='1' value="1" disabled /> <br/> <button type="button" class="add_btn" disabled>Add more products</button> <div id="clear"></div> </div> </div> </div> <div id="clear"></div>
希望我能進一步推動您。
不幸的是,您在代碼中犯了很多錯誤。 因此,我將僅描述重要的錯誤:
id
屬性在完整的HTML頁面中必須是唯一的。 如果克隆某個元素並且它具有id屬性,則必須創建一個新的id。 並且因為您希望從按鈕中刪除id
屬性-所以我為您做了。 element.disable
禁用元素。 大多數瀏覽器都支持它,但這不是標准。 如果查看Element
和Node
的文檔,則將看到它們不具有此屬性。 某些瀏覽器不支持。 在這種情況下,使用Element.setAttribute()
和Element.removeAttribute()
函數。 float: left
如果不需要,請使用float: left
。 在您的情況下,您不需要它,因為您有inline-block
元素。 我也更改了很多CSS代碼。 addEventListener
方法而不是內聯事件偵聽器,但在您的情況下,這是有爭議的,因此,我不使用addEventListener
,因此您可以更好地理解我的代碼。 但是我在您的函數中添加了一個參數–請注意。 我編寫了新代碼,以便您沒有任何類型的錯誤。 好好享受!
完整的解決方案
var productsByCategory = { A: ["Select sub-product", "CNC 1", "CNC 2", "CNC 3", "CNC 4"], B: ["Select sub-product", "LASER 1", "LASER 2", "LASER 3", "LASER 4"], C: ["Select sub-product", "RUBBER 1", "RUBBER 2", "RUBBER 3", "RUBBER 4", "RUBBER 5"], D: ["Select sub-product", "PRECISION 1", "PRECISION 2", "PRECISION 3"] }; var valuesByCategory = { A: ["", "A1", "A2", "A3", "A4"], B: ["", "B1", "B2", "B3", "B4"], C: ["", "C1", "C2", "C3", "C4", "C5"], D: ["", "D1", "D2", "D3"] }; //var no_of_products = 1; //WE DO NOT NEED IT function selectHelper(category, targetObj) { for(var i in productsByCategory[category]) { var option = document.createElement("option"); option.text = productsByCategory[category][i]; option.value = valuesByCategory[category][i]; targetObj.add(option); } setEnabled(targetObj); } function dropdown(obj) { var selected = obj.value, //second select: target = obj.nextElementSibling; for(var i = target.length; i--; ) target.remove(i); if(selected == 0) { var option = document.createElement("option"); option.text = "Select Product first"; option.value = ""; target.add(option); setDisabled(target); //set disabled input field: setDisabled(target.nextElementSibling) } else { if(selected == 1) selectHelper('A', target); else if(selected == 2) selectHelper('B', target); else if(selected == 3) selectHelper('C', target); else selectHelper('D', target) } } function dropdown2(obj) { setEnabled(obj.nextElementSibling); setEnabled(document.getElementsByClassName('add_btn')[0]); } function new_products() { var allProducts = document.getElementsByClassName('product'); lastProduct = allProducts[allProducts.length - 1], clone = lastProduct.cloneNode(true); setDisabled(clone.getElementsByClassName('second_select')[0]); //set disabled input field: setDisabled(clone.getElementsByClassName('s_btn')[0]); setDisabled(document.getElementsByClassName('add_btn')[0]); //may be "insertBefore" is weird, but we do here insert new product after the last product: document.getElementById('order_now').insertBefore(clone, lastProduct.nextSibling); } function setDisabled(obj) { obj.setAttribute("disabled", "disabled"); } function setEnabled(obj) { obj.removeAttribute("disabled"); }
body { height: 100vh; margin: 0px; overflow-y: auto; font-family: 'Roboto'; } .content { background-color: white; height: auto; margin-top: 0px; z-index: -1; min-height: 88%; } .link-contents { position: relative; left: 0px; width: 100%; } .option-links { display: block; font-size: 30px; cursor: pointer; } #op1 {background-color: #cccccc} select, button, input { position: relative; top: 5em; width: 12em; height: 2em; } button {width: 8em} .first_select { position: relative; left: 10%; } .second_select { position: relative; left: 20%; } .s_btn { position: relative; left: 30%; } .add_btn { top: 6em; width: 10em; } .footer { display: block; max-height: 4%; } .option-contents {display: none} #order_now {display: block}
<link href='https://fonts.googleapis.com/css?family=Roboto' rel='stylesheet'> <link rel="stylesheet" type="text/css" href="profile.css"> <div class="content"> <div class="link-contents"> <div class="option-contents" id="order_now"> <div class="product"> <select class="first_select" onchange="dropdown(this)"> <option value="0">Select</option> <option value="1">CNS</option> <option value="2">Laser Cut</option> <option value="3">Rubber roller</option> <option value="4">Fixture</option> </select> <select class="second_select" onchange="dropdown2(this)" disabled> <option>Select Product first</option> </select> <input class="s_btn" type="number" min='1' value="1" disabled /> </div> <center><button class="add_btn" onclick="new_products()" disabled>Add more products</button></center> </div> </div> </div> <div class="footer">A big thank you to all of you.</div>
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.