I'm creating a form where fields can be added by the user. Press a button and more input fields are added. The id of the cloned fields has a number appended to it to distinguish it from the original fields.
The selected option in the elements in the original fields is resetting to the first option when the cloned elements get appended.
I wrote some simplified code that demonstrates the same behavior:
HTML:
<!DOCTYPE html>
<head>
<script src="cloneTest.js" crossorigin="anonymous"></script>
</head>
<body>
<div id="cloneParent">
<div id="cloneThis">
<select id="cloneTestSelect">
<option value="Option1">Option1</option>
<option value="Option2">Option2</option>
<option value="Option3">Option3</option>
<option value="Option4">Option4</option>
</select>
<label for="cloneTestText">Text Input: </label>
<input type="text" id="cloneTestText">
<label for="cloneTestNumber">Number Input: </label>
<input type="text" id="cloneTestNum">
</div>
</div>
<br>
<h1>Cloned fields: </h1>
<br>
<div id="clonedFields"></div>
<button onclick="cloneIt()">Clone inputs</button>
</body>
Javascript:
function cloneIt(){
let newFields = document.getElementById('clonedFields');
let cloneThis = document.getElementById('cloneThis');
let clonedNode = cloneThis.cloneNode(true);
newFields.parentNode.appendChild(clonedNode);
}
The input elements retain their values, but select is reset. Is there a way to retain the select value outside of storing it in a separate variable? Or stop it from being reset?
CodePen link: https://codepen.io/whisperV/pen/rXqGNM
The problem is that, when you clone a node, you effectively take its HTML markup and create a new element out of that markup. A selected <option>
does not have any of its HTML attributes change in response to being selected, unless you do so explicitly (by giving the selected <option>
a selected
attribute, and by making sure no other <option>
tags have said attribute).
const origSelect = document.querySelector('select');
const options = [...origSelect.options];
origSelect.addEventListener('change', () => {
options.forEach(option => option.removeAttribute('selected'));
options[origSelect.selectedIndex].setAttribute('selected', '');
});
function cloneIt() {
let newFields = document.getElementById('clonedFields');
let cloneThis = document.getElementById('cloneThis');
let clonedNode = cloneThis.cloneNode(true);
clonedNode.querySelector('select').value = origSelect.value;
newFields.parentNode.appendChild(clonedNode);
}
<div id="cloneParent">
<div id="cloneThis">
<select id="cloneTestSelect">
<option value="Option1">Option1</option>
<option value="Option2">Option2</option>
<option value="Option3">Option3</option>
<option value="Option4">Option4</option>
</select>
<label for="cloneTestText">Text Input: </label>
<input type="text" id="cloneTestText">
<label for="cloneTestNumber">Number Input: </label>
<input type="text" id="cloneTestNum">
</div>
</div>
<br>
<h1>Cloned fields: </h1>
<br>
<div id="clonedFields"></div>
<button onclick="cloneIt()">Clone inputs</button>
While you could do that, It'd be easier to simply retrieve the current value of the <select>
, and set the new select tag to the original select's value:
const origSelect = document.querySelector('select');
function cloneIt() {
let newFields = document.getElementById('clonedFields');
let cloneThis = document.getElementById('cloneThis');
let clonedNode = cloneThis.cloneNode(true);
clonedNode.querySelector('select').value = origSelect.value;
newFields.parentNode.appendChild(clonedNode);
}
<div id="cloneParent">
<div id="cloneThis">
<select id="cloneTestSelect">
<option value="Option1">Option1</option>
<option value="Option2">Option2</option>
<option value="Option3">Option3</option>
<option value="Option4">Option4</option>
</select>
<label for="cloneTestText">Text Input: </label>
<input type="text" id="cloneTestText">
<label for="cloneTestNumber">Number Input: </label>
<input type="text" id="cloneTestNum">
</div>
</div>
<br>
<h1>Cloned fields: </h1>
<br>
<div id="clonedFields"></div>
<button onclick="cloneIt()">Clone inputs</button>