Dynamically Clone Element when User Specifies amount? - JavaScript + HTML - javascript

Is it possible for a user to type in an <input> box the amount of an element they would like to be cloned and append on the page?
The problem is, when a user currently specifies the amount of elements to be cloned, the number of elements is greater than that wanting to be cloned on the second click. Also, is it possible to hide the original element to be cloned within HTML and CSS? Please find attached the code below and the JsFiddle Link.
$("#btn").click(function() {
var number = document.getElementById("member").value;
var e = $('.col');
for (i=0;i<number;i++) {
e.clone().insertAfter(e);
}
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
Specify Amount of Member Cards Required:<input placeholder="e.g. 2" type="text" id="member" name="member" value="">
<button id="btn" onclick="addinputFields()">Go</button>
<div>
</div>
<br>
<div class="col">Member Card</div>

Slight tweak on the HTML and Javascript.
Added a jsfiddle example of the code below.
HTML
Specify Amount of Member Cards Required:
<input placeholder="e.g. 2" type="text" id="member" name="member" value="">
<button id="btn">Go</button>
<div id="colTemplate" class="col">Member Card</div>
<div id="container"></div>
CSS
/* hide the template */
#colTemplate { opacity: 0; visibility: hidden; }
Javascript
$('#btn').click(() => {
let number = document.getElementById('member').value,
container = $('#container'),
template = $('#colTemplate'),
currentNumber = container.children().length;
if (currentNumber < number) {
// Add clones
for (let i = 0; i < number - currentNumber; i++) {
let clone = template.clone(true);
clone.attr('id', null); // Remove the id from the clone
container.append(clone);
}
} else if (number < currentNumber) {
// Remove extras
let extras = $('#container .col');
for (let i = currentNumber - 1; i > number - 1; i--) {
extras.eq(i).remove();
}
}
});

Use :first selector to clone only the first
$('.col:first').hide();
$("#btn").click(function() {
var number = $("#member").val();
var e = $('.col:first');
$('.col').not('.col:first').remove();
for (i=0;i<number;i++) {
e.show().clone().insertAfter(e);
}
$('.col:first').hide();
});
demo:https://jsfiddle.net/j4jmyh0q/

Related

Generate Dynamic Inputs with Unique Names in jQuery

In my HTML form, it's possible to add additional inputs dynamically by clicking a button. I've got this part to work, however I need each input to have a unique name.
This is my code so far,
<div class="control-group field_wrapper">
<label class="control-label"><strong> Phone Number 1</strong></label>
<input type="text" class="input-medium" name="phone_number[]">
<button class="btn btn-success add-number" type="button" title="Add">Add</button>
</div>
<div class="additionalNumber"></div>
My JS as below,
$(document).ready(function(){
var maxField = 10;
var addButton = $('.add-number');
var wrapper = $('.additionalNumber');
function fieldHTML(inputNumber) {
return `<div class="control-group field_wrapper">\
<label class="control-label"><strong> Phone Number ${inputNumber}</strong></label>\
<input type="text" class="input-medium" name="phone_number[${inputNumber}]">\
<button class="btn btn-danger remove" type="button">Remove</button>\
</div>`;
}
var x = 1;
$(addButton).on('click', function(e) {
if (x < maxField) {
x++;
$(wrapper).append(fieldHTML(x));
}
if (x >= maxField) {
alert('Limited to 10.');
}
});
$(wrapper).on('click', '.remove', function(e) {
e.preventDefault();
$(this).parents('.control-group').remove();
x--;
});
});
Using this code, I can get unique name for each input which are created by dynamically. But my problem is name[x] index not works properly when it is removing. That mean, just think I have added 3 input and delete second one and again I am adding new one, then it has same name twice. In this case, it is phone_number[3] for second input and phone_number[3] for thirt one also.
This is the fiddle from above code. Any help is appreciated.
You don't need to index the inputs for PHP either - 3x inputs named phone_number[] will automatically be indexed 0 - 2 on the back end:
<input type="text" name="phone_number[]">
<input type="text" name="phone_number[]">
<input type="text" name="phone_number[]">
[phone_number] => Array
(
[0] => a
[1] => b
[2] => c
)
That doesn't help with your plain text Phone Number n label though. And maybe you have your own reasons to want an input name index.
If you think about it, if you're going to allow deletions of any item in the list, and you need the results to be sequential, the only option is to renumber everything each time you make a change. You don't need to include any numbering when you add a new element, just regenerate all numbering.
Here's a working snippet doing that. My changes:
No need to pass the count x to fieldHTML(), we're going to renumber everything after you add the element;
Add a <span class='count'></span> in your label, which we can target and update;
Add a reNumber() function which will iterate over all inputs on the page and number them sequentially;
Call that function after any change;
Notes:
The 2 separate tests if (x < maxField) and if (x >= maxField) can be combined into a single if/else;
If you want to get rid of the duplication of your HTML block, you could give the first one an id like template, and then instead of duplicating that HTML in your JS, just copy the template, eg :
let $copy = $('#template').clone();
wrapper.append($copy);
wrapper and addButton are already jQuery objects, no need to wrap them with $() a second time to use them;
If you do want to number your input names, for consistency the first should probably be phone_number[1];
$(document).ready(function() {
var x = 1;
var maxField = 10;
var addButton = $('.add-number');
var wrapper = $('.additionalNumber');
function fieldHTML() {
return `<div class="control-group field_wrapper">\
<label class="control-label"><strong> Phone Number <span class='count'></span></strong></label>\
<input type="text" class="input-medium" name="phone_number[]">\
<button class="btn btn-danger remove" type="button">Remove</button>\
</div>`;
}
/**
* Iterate over all inputs and renumber sequentially
*/
function reNumber() {
let count;
wrapper.find('.field_wrapper').each(function (i) {
// .each() index is 0-based, and input #1 is already on the page,
// so extras start at #2
count = i + 2;
$('.count', $(this)).html(count);
// If you want to index your input names, but you can safely leave
// this out, PHP will index them anyway
$('input', $(this)).attr('name', 'phone_number[' + count + ']')
});
}
addButton.on('click', function(e) {
if (x < maxField) {
x++;
wrapper.append(fieldHTML());
reNumber();
} else {
alert('Limited to 10.');
}
});
wrapper.on('click', '.remove', function(e) {
e.preventDefault();
$(this).parents('.control-group').remove();
x--;
reNumber();
});
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="control-group field_wrapper">
<label class="control-label"><strong> Phone Number 1</strong></label>
<input type="text" class="input-medium" name="phone_number[]">
<button class="btn btn-success add-number" type="button" title="Add">Add</button>
</div>
<div class="additionalNumber"></div>

Increment and update value in the total number after insert new rows dynamically

EDIT: I have updated the code with the answers.
I have a increment function that is working fine. However:
1. I would like to set some limits based on the total number available in one of the span. For example, 10. So the incrementing can't be more than 10. #DONE
Another issue is that I am planning to have multiple rows and before I save I want to make sure if we count the increments in every row it should not be more than 10 as well. If it decrease the total number (span) dynamically would be nice.
I'm adding rows dynamically with the ADD button, how can I add news rows that actually work with the current functions? Mine rows just clone the first one and the increment function is disabled.
document.addEventListener('DOMContentLoaded', async function() {
document.querySelector('#addlocationdest').addEventListener('click', add);
});
function add() {
var x = 1;
var container = document.getElementById('destination');
var detail = document.getElementById('row');
var clone = detail.cloneNode(true);
clone.id = "destination" + x;
x++;
container.appendChild(clone);
}
window.addEventListener("load", () => {
let elTotalQuantity = document.querySelector("#totalqty");
let totalQuantity = parseInt(elTotalQuantity.innerHTML);
function getSumOfRows() {
let sum = 0;
for (let input of document.querySelectorAll("form .row > input.quantity"))
sum += parseInt(input.value);
return sum;
}
for (let row of document.querySelectorAll("form .row")) {
let input = row.querySelector("input");
row.querySelector(".increment").addEventListener("click", () => {
if (getSumOfRows() >= totalQuantity) return;
input.value++;
elTotalQuantity.innerHTML = totalQuantity - getSumOfRows();
});
row.querySelector(".decrement").addEventListener("click", () => {
if (input.value <= 0) return;
input.value--;
elTotalQuantity.innerHTML = totalQuantity - getSumOfRows();
});
}
});
<div id="location" class="hide">
<div class="title">Transfer details</div><br>
<div class="line padded-s">Total Quantity: <span>10</span></div>
<br>
<form>
<label>New Total Quantity at this location: <span id="totalqty">10</span></label>
<br>
<div id="destination">
<div id="row" class="row">
<button type="button" class="decrement">-</button>
<input type="text" class="quantity" value="0" readonly/>
<button type="button" class="increment">+</button>
<a>Location: </a>
<input type="text" class="location" value="0" readonly/>
</div>
</div>
</form>
<label>Total being transfer: <p id="total-sum"></p></label>
<br>
<button type="button" id="addlocationdest">ADD</button>
<button type="button" id="removelocationdest">REMOVE</button>
</div>
Prologue
As long as the total quantity is fixed at the beginning of the script-execution, this works. Otherwise, it would be best to save the actual allowed total quantity as an attribute, and observe it using a MutationObserver. That way you can update your max. value in your code dynamically, when the total quantity-attribute changes. You can define custom attributes by naming them "data-*" where "*" is a custom name.
Solution for your problem
You are using the same ID on multiple elements. What you meant were classes, so change id="increment" to class="increment", and the same for decrement.
Since we don't want to input something with the buttons, but add listener to them, I'd say it is better to actually use <button>. In forms, buttons act as type="submit", which we don't want, so we need to change it to type="button".
Since the rows and the total quantity actually belong together, it is wiser to place them together into one <form>-element. However, you can still group the buttons and inputs as a row together using <div>.
Now regarding the in-/decrementing of the row's values and the total quantity:
Save the allowed total quantity in a variable
Add event-listener to the corresponding buttons
If action is valid, change row's value
Update total quantity number to totalQuantity - getSumOfRows()
To add new rows dynamically, we create and setup such an element, and append it to the form. See the appendNewRow()-function below.
Sidenote
I have added the readonly attribute to the input-fields so that you cannot enter numbers via keyboard.
window.addEventListener("load", () => {
let elTotalQuantity = document.querySelector("#totalqty");
let totalQuantity = parseInt(elTotalQuantity.innerHTML);
function getSumOfRows() {
let sum = 0;
for (let input of document.querySelectorAll("form .row > input.quantity"))
sum += parseInt(input.value);
return sum;
}
function updateTotalQuantity() {
elTotalQuantity.innerHTML = totalQuantity - getSumOfRows();
}
function appendNewRow() {
let row = document.createElement("div");
row.classList.add("row");
let child;
// input.quantity
let input = document.createElement("input");
input.classList.add("quantity");
input.value = "0";
input.setAttribute("readonly", "");
input.setAttribute("type", "text");
row.append(input);
// button.increment
child = document.createElement("button");
child.classList.add("increment");
child.innerHTML = "+";
child.setAttribute("type", "button");
child.addEventListener("click", () => {
if (getSumOfRows() >= totalQuantity) return;
input.value++;
updateTotalQuantity();
});
row.append(child);
// button.increment
child = document.createElement("button");
child.classList.add("decrement");
child.innerHTML = "-";
child.setAttribute("type", "button");
child.addEventListener("click", () => {
if (input.value <= 0) return;
input.value--;
updateTotalQuantity();
});
row.append(child);
// button.remove-row
child = document.createElement("button");
child.classList.add("remove-row");
child.innerHTML = "Remove";
child.setAttribute("type", "button");
child.addEventListener("click", () => {
row.remove();
updateTotalQuantity();
});
row.append(child);
document.querySelector("form .rows").append(row);
}
document.querySelector("form .add-row").addEventListener("click", () => appendNewRow());
appendNewRow();
});
<form>
<label>Total Quantity: <span id="totalqty">10</span></label>
<br>
<div class="rows">
</div>
<button type="button" class="add-row">Add new row</button>
</form>
QuerySelector only selects the first occurrence so you haven't really added a listener to the second "row". You should use querySelectorAll but, instead of unique ids, use classes.
<input class="increment" type="button" value="+" />
Now you can use document.querySelectorAll(".increment") to get all elements in an array.
You can traverse in the DOM by using parentElement. By knowing which button you clicked, you can traverse up to the form element and then select the first child - which is an input. A more dynamic way would be to use querySelector to select the input, in case the HTML change in the future. Anyway, that's how you can know which input to manipulate based on where the buttons are in the DOM.
I added two global variables, totalSum and maxSum. maxSum is fetched from your span element (which I assigned an unique id to). totalSum makes sure that all inputs combined doesn't exceed maxSum.
You had some duplicate code, so I refactored it into a new method: changeValue.
In all, I think the code speaks for itself.
Oh, this code doesn't take into account that the user can change the value inside the input. I will leave that for you to figure out with an "oninput" listener on each text input.
var totalSum = 0; // 3
var maxSum = 0
var totalSumElement = null;
document.addEventListener('DOMContentLoaded', async function() {
totalSumElement = document.getElementById('total-sum');
maxSum = document.getElementById('max-sum').innerText;
var incrementElements = document.querySelectorAll('.increment'); // 1
var decrementElements = document.querySelectorAll('.decrement');
addListener('click', incrementElements, incrementValue);
addListener('click', decrementElements, decrementValue);
});
function addListener(type, elementArr, func) {
for (element of elementArr) {
element.addEventListener(type, func);
}
}
function withinRange(newValue) {
var maxReached = newValue > maxSum; // 3
var zeroReached = newValue < 0;
return !maxReached && !zeroReached;
}
function changeValue(event, change) { // 4
if (withinRange(totalSum + change)) {
let parent = event.currentTarget.parentElement; // 2
let input = parent.children[0];
let value = parseInt(input.value) || 0;
if (withinRange(value + change)) {
input.value = value + change;
totalSum = totalSum + change;
}
}
totalSumElement.textContent = `Total: ${totalSum}`;
}
function incrementValue(event) {
changeValue(event, 1);
}
function decrementValue(event) {
changeValue(event, -1);
}
#totalqty {
padding-bottom: 1rem;
}
<div id="totalqty" class="line padded-s">Total Quantity: <span id="max-sum">10</span></div>
<form>
<input type="text" value="0" />
<input class="increment" type="button" value="+" />
<input class="decrement" type="button" value="-" />
</form>
<form>
<input type="text" value="0" />
<input class="increment" type="button" value="+" />
<input class="decrement" type="button" value="-" />
</form>
<p id="total-sum"></p>

Changing two input values to one

I am trying to change the two input boxes from feet/inches to one input box to centimeters when centimeters is selected as an option. It works with feet and inches but I want only one input when user selects centimeters. How would I able to change it using javascript?
<form action=" " onsubmit="">Enter your Height:
<input type="text" id="height"> </input>
Feet <input type="text" id="height2"> </input>Inches
<select id = "typeH">
<option value = "feet">Feet/Inches</option>
<option value = "cm">Centimeter</option>
</select>
Here is one of the function in my js code:
function calculateBMI() {
var weight = document.getElementById('weight').value;
var height = document.getElementById('height').value;
var height2 = document.getElementById('height2').value;
var typeW = document.getElementById("type").value;
var typeH = document.getElementById("typeH").value;
if (typeW === "lbs") {
weight = weight * 0.45;
} else if (typeW === "kg") {
weight = weight;
}
if (typeH === "feet") {
height = height * 0.3048;
height2 = height2 * 0.0254;
var totalheight = height + height2;
} else if (typeH === "cm") { //this is the part where i want to change the two input boxes to one if the user selects cm as type of height
typeH.addEventListner("click", change());
//change();
height = height * 0.0328084;
height2 = 0;
var totalheight = height + height2;
}
var total = weight / (totalheight * totalheight);
roundToTwo(total);
document.getElementById('result').value = roundToTwo(total);
}
You'll need some javascript. Here is a quick and dirty solution, also using CSS:
document.getElementById('typeH').addEventListener('change', function(e) {
var el = e.target;
// Mark all of them as hidden
for(var i = 0; i < el.options.length; i++) {
var spanEl = document.getElementById(el.options[i].value);
spanEl.className = 'hidden';
// Reset all of the input options
var inputs = spanEl.querySelectorAll('input');
for(var j = 0; j < inputs.length; j++) {
inputs[j].value = '';
}
}
// Show the one that was selected
document.getElementById(el.options[el.selectedIndex].value).className = '';
});
.hidden {
display: none;
}
<form action="" onsubmit="">
<span>Enter your Height:</span>
<span id="feet"><!-- This id must match the value in the select option -->
<input type="text" id="heightInFeet" name="heightInFeet"></input> Feet
<input type="text" id="heightInInches" name="heightInInches"></input> Inches
</span>
<span id="cm" class="hidden">
<input type="text" id="heightInCentimeters"></input> Centimeters
</span>
<select id="typeH">
<option value="feet">Feet/Inches</option><!-- This value must match the id of the span -->
<option value="cm">Centimeters</option>
</select>
</form>
In the Javascript, we add an event listener to the change event of the #typeH select element. (This is the equivalent of putting a function in the onChange attribute inline in the HTML.)
In the CSS, we just add a class that hides the irrelevant elements.
In the HTML, we reorganize the DOM tree so the sections of inputs and text (namely for feet/inches and centimeters) into wrapper span elements. That way in javascript, we can hide all of the inputs in the spans, and then "show" (or rather, remove the hidden class) on the selected option.
Hopefully this is educational for you.
Apart from the code you are using to calculate the weight and height values, you need to use another code to handle the hiding/showing case.
You need to bind an onchange event on the select element where, you will hide/show the second input according to the selected value.
In your JS create the following function:
function filterHeight(select) {
if (select.value !== "feet") {
document.getElementById("height2").style.display = "none";
} else {
document.getElementById("height2").style.display = "block";
}
}
Where document.getElementById("height2") is used to refer the second input and .style.display is used to hide/show it.
Then in your HTML add onchange="filterHeight(this)" to the select element:
<select id="typeH" onchange="filterHeight(this)">
Where this refers to the select element itself, so you can access its value inside the callback function.
Demo:
This is a working demo snippet:
function filterHeight(select) {
if (select.value !== "feet") {
document.getElementById("height2").style.display = "none";
} else {
document.getElementById("height2").style.display = "block";
}
}
<form action=" " onsubmit="">Enter your Height:
<input type="text" id="height"> </input>
Feet <input type="text" id="height2"> </input>Inches
<select id="typeH" onchange="filterHeight(this)">
<option value="feet">Feet/àInches</option>
<option value="cm">Centimeter</option>
</select>

How to change background color of input based on entered value

I'm trying to change the background color of an input field based on the value entered. I have some JS that details some greater than/less than conditions.
Question - It is only working in the first cell and not all the rest. How do I get the script to pass through the other input values?
HTML snippet:
<div class="row">
<div class="col-lg-3 text-center">
<label for="k-cls" class="text-strong">CLS</label>
<input class="form-control" type="text" id="k-cls" placeholder="#" onchange="checkFilled();">
</div>
</div>
Here's the JS snippet:
function checkFilled() {
var inputVal = document.getElementById("k-cls");
if (inputVal.value >= 6 && inputVal.value <= 9) {
inputVal.style.backgroundColor = "yellow";
}
else{
inputVal.style.backgroundColor = "";
}
}
checkFilled();
Here's a current working Codepen.
First, you cannot have multiple elements on one page with the same ID - so you should make sure to fix that. Classes are fine to be reused. Read more about the differences between ID and class at CSS-Tricks.
Second, this can be accomplished agnostic of an element's ID/class/etc. by simply passing a reference of the onchange event to your function, as seen below.
(I've also swapped onchange out for onkeyup so you can see the function run immediately.)
EDIT: Per discussion in the comments below, additional functionality has been added to this demo.
function checkFilled(event) {
var inputVal = event.target;
var inputData = JSON.parse(inputVal.dataset.ranges);
var color = "";
for (i = 0; i < inputData.length; i++) {
if (inputVal.value >= inputData[i][0] && inputVal.value <= inputData[i][1]) {
color = inputData[i][2];
break;
}
}
inputVal.style.backgroundColor = color;
}
<input
placeholder="#"
data-ranges='[[1, 3, "yellow"], [4, 6, "blue"], [7, 9, "red"]]'
onkeyup="checkFilled(event);">

Reveal additional info based on two (out of three) checkboxes JavaScript

I'm new at Javascript and I'm trying to reveal additional info only if any 2 out of 3 checkboxes are checked.
Here is my code so far (I'm trying to enter my code in the question but It's not working, sorry. I also may have made it more complicated then necessary, sorry again). I did place my code in the Demo.
<script>
var checkboxes;
window.addEvent('domready', function() {
var i, checkbox, textarea, div, textbox;
checkboxes = {};
// link the checkboxes and textarea ids here
checkboxes['checkbox_1'] = 'textarea_1';
checkboxes['checkbox_2'] = 'textarea_2';
checkboxes['checkbox_3'] = 'textarea_3';
for ( i in checkboxes ) {
checkbox = $(i);
textbox = $(checkboxes[i]);
div = $(textbox.id + '_container_div');
div.dissolve();
showHide(i);
addEventToCheckbox(checkbox);
}
function addEventToCheckbox(checkbox) {
checkbox.addEvent('click', function(event) {
showHide(event.target.id);
});
}
});
function showHide(id) {
var checkbox, textarea, div;
if(typeof id == 'undefined') {
return;
}
checkbox = $(id);
textarea = checkboxes[id];
div = $(textarea + '_container_div');
textarea = $(textarea);
if(checkbox.checked) {
div.setStyle('display', 'block');
//div.reveal();
div.setStyle('display', 'block');
textarea.disabled = false;
} else {
div.setStyle('display', 'none');
//div.dissolve();
textarea.value = '';
textarea.disabled = true;
}
}
<label for="choice-positive">
<script type="text/javascript">
function validate(f){
f = f.elements;
for (var c = 0, i = f.length - 1; i > -1; --i)
if (f[i].name && /^colors\[\d+\]$/.test(f[i].name) && f[i].checked) ++c;
return c <= 1;
};
</script>
<label>
<h4><div style="text-align: left"><font color="black">
<input type="checkbox" name="colors[2]" value="address" id="address">Full Address
<br>
<label>
<input type="checkbox" name="colors[3]" value="phone" id="phone">Phone Number <br>
<label>
<input type="checkbox" name="colors[4]" value="account" id="account">Account Number <br>
</form>
<div class="reveal-if-active">
<h2><p style = "text-decoration:underline;"><font color="green">Receive the 2 following
pieces of info:</h2></p>
</style>
Sorry i wasn't able to exactly use the code you provided but tried to change just enough to get it working.
I've uploaded a possible solution to JSFiddle - you essentially can add event listeners to the checkboxes that recheck when clicked how many are selected and show/hide via removing/adding a class e.g. additionalContactBox.classList.remove('reveal-if-active');

Categories

Resources