i am developing a form where you should be able to generate dynamically textfields based on a button and delete if necessary, the wrong thing thing starts when i try to delete the whole DIV element i cloned but instead it only deletes the delete button and the textbox stays there and can't be deleted since the div containing the delete button is the one is deleted, the delete button is in the same div as the textbox, before i forget i am doing all of this using bootstrap 4 so i can't use classes for references as i have seen in some examples, i'll write the codeso you can see what i could be doing wrong
HTML CODE
<div class="form-group" name="CamposTexto" id="CamposTexto"> //whole div i desire to clone
<label class="col-sm-2 control-label" id="label_situacion_actual">Situación Actual:<a id="contador"></a></label>
<div class="col-sm-8">
<textarea id="situacion_permiso_precario" name="situacion_permiso_precario"
class="form-control input-sm" style="resize:none;height:60px;text-transform:uppercase;"
maxlength="500" onKeyPress="return SoloNumeros(event);"></textarea>
</div>
<div id="quitarCampoDeTexto" name="quitarCampoDeTexto"> //this is the div that disappears when i press the X to delete, only happens when i cloned the div
<button type="button" onClick="quitarCampoDeTexto(this);" class="btn btn-danger glyphicon glyphicon-remove"></button>
</div>
</div>
<div id="AgregarCampos" class="form-group" style="margin-bottom:10px;"></div> //reserved space for the cloned div to appear
<div id="Botones" style="margin-bottom:10px;">
<center>
<label>Agregar Más</label> //add more button
<button type="button" class="btn btn-success glyphicon glyphicon-plus"
onClick="agregarCampoDeTexto('CamposTexto', 'AgregarCampos');" id="botonAgregar" name="botonAgregar"></button>
</center>
</div>
JS
var contador = 1;
function agregarCampoDeTexto(divName, CamposTexto) {
if (contador == 25) {
document.getElementById("botonAgregar").remove();
} else {
var newdiv = document.createElement('div');
newdiv.innerHTML = document.getElementById(divName).innerHTML;
newdiv.className = 'form-group';
newdiv.id = 'situacion_permiso_precario';
newdiv.name = 'situacion_permiso_precario';
document.getElementById(CamposTexto).appendChild(newdiv);
contador++;
}
var selectElements = document.querySelectorAll('textarea');
for (var i = 1; i < selectElements.length; i++) {
selectElements[i].id = 'situacion_permiso_precario_' + i;
selectElements[i].name = 'situacion_permiso_precario_' + i;
document.getElementById("label_situacion_actual").innerHTML = "Situación Actual:" + i;
}
}
function quitarCampoDeTexto(obj) {
if (contador <= 1) {
return false;
}else{
obj.parentNode.Name == 'form-group'
obj.parentNode.parentNode.removeChild(obj.parentNode);
contador--;
}
}
I have been stuck with this for a couple of days, any kind of help would be great, thanks in advance
EDIT
Labels or div counters are not being displayed properly
Remove the parent of parent using double .parentNode
var contador = 1;
function agregarCampoDeTexto(divName, CamposTexto) {
if (contador == 25) {
document.getElementById("botonAgregar").remove();
} else {
var newdiv = document.createElement('div');
newdiv.innerHTML = document.getElementById(divName).innerHTML;
newdiv.className = 'form-group';
newdiv.id = 'situacion_permiso_precario';
newdiv.name = 'situacion_permiso_precario';
document.getElementById(CamposTexto).appendChild(newdiv);
contador++;
}
var selectElements = document.querySelectorAll('textarea');
for (var i = 1; i < selectElements.length; i++) {
selectElements[i].id = 'situacion_permiso_precario_' + i;
selectElements[i].name = 'situacion_permiso_precario_' + i;
document.getElementById("label_situacion_actual").innerHTML = "Situación Actual:" + i;
}
}
function quitarCampoDeTexto(obj) {
if (contador <= 1) {
return false;
}else{
obj.parentNode.Name == 'form-group';
obj.parentNode.parentNode.remove();
contador--;
}
}
<div class="form-group" name="CamposTexto" id="CamposTexto">
<label class="col-sm-2 control-label" id="label_situacion_actual">Situación Actual:<a id="contador"></a></label>
<div class="col-sm-8">
<textarea id="situacion_permiso_precario" name="situacion_permiso_precario"
class="form-control input-sm" style="resize:none;height:60px;text-transform:uppercase;"
maxlength="500" onKeyPress="return SoloNumeros(event);"></textarea>
</div>
<div id="quitarCampoDeTexto" name="quitarCampoDeTexto">
<button type="button" onClick="quitarCampoDeTexto(this);" class="btn btn-danger glyphicon glyphicon-remove"></button>
</div>
</div>
<div id="AgregarCampos" class="form-group" style="margin-bottom:10px;"></div>
<div id="Botones" style="margin-bottom:10px;">
<center>
<label>Agregar Más</label>
<button type="button" class="btn btn-success glyphicon glyphicon-plus"
onClick="agregarCampoDeTexto('CamposTexto', 'AgregarCampos');" id="botonAgregar" name="botonAgregar"></button>
</center>
</div>
Related
I am trying to learn some javascript in web programming. Starting with a simple school registration webpage: the webpage allows to dynamically create any number of grades by clicking "Grade+" button; under each grade, any number of students can be created by clicking "Student+" button. "Grade+" button works as expected, however clicking "Student+" button does not present the student information, not sure what is happening. Any help will be highly appreciated. Thanks in advance.
The reference codes:
<!DOCTYPE html>
<html>
<body>
<div>
<label>Registration</label>
<div class="form-inline justify-content-center" id="school" style="display:none">
<label for="fname">Grade:</label>
<input type="text" id="grade" name="Grade"><br><br>
<div id="students">
<div id="student">
<label for="fname">First:</label>
<input type="text" id="first" name="First"><br><br>
<label for="lname">Last:</label>
<input type="text" id="last" name="Last"><br><br>
</div>
<div class="text-center" id="add_student">
<span id="idStudentRootCopy">----S----</span>
<button type="button" onclick="addItem('student', 'idGradeRootCopy', false)">Student+</button>
</div>
</div>
</div>
<div class="text-center" id="add_grade">
<span id="idGradeRootCopy">----G----</span>
<button type="button" onclick="addItem('school', 'idGradeRootCopy', true)">Grade+</button>
</div>
</div>
<script>
var count = 0;
function addItem(id, index, root) {
var original = document.getElementById(id);
var before = document.getElementById(index);
var clone = original.cloneNode(true);
clone.style.display='block';
clone.id = id + ++count;
var newFields = clone.childNodes;
for (var i = 0; i < newFields.length; i++) {
var fieldName = newFields[i].name;
if (fieldName)
newFields[i].name = fieldName + count;
}
if (root) {
original.parentNode.insertBefore(clone, before.parentNode);
} else {
original.insertBefore(clone, before);
}
}
</script>
</body>
</html>
If you open up the developer tools of your browsers and click the Student+ button you'll get an error message like:
Uncaught DOMException: Node.insertBefore: Child to insert before is
not a child of this node
So you're actually trying to put the cloned node into the wrong spot. Either way things are a bit confusing. Let's say you have clicked the Grade+ button three times and now you decide to click on Student+ of the first clone - how should it know where to put the student as there are three grades?
Well there's a fix of course. Each Student+ button is a child of an unique clone of the school <div> which you also gave an unique id yet (school1, school2,...). So if you pass the addItem() function a reference to the button you actually clicked, we can get it's parent div like:
clickedElement.parentNode.parentNode.parentNode
and add the cloned node using appendChild() instead of insertBefore().
Here's an example (just click on 'Run code snippet'):
var count = 0;
function addItem(id, index, root, clickedElement) {
var original = document.getElementById(id);
var before = document.getElementById(index);
var clone = original.cloneNode(true);
clone.style.display = 'block';
clone.id = id + ++count;
var newFields = clone.childNodes;
for (var i = 0; i < newFields.length; i++) {
var fieldName = newFields[i].name;
if (fieldName)
newFields[i].name = fieldName + count;
}
if (root) {
original.parentNode.insertBefore(clone, before.parentNode);
} else {
clickedElement.parentNode.parentNode.parentNode.appendChild(clone);
}
}
<div>
<label>Registration</label>
<div class="form-inline justify-content-center" id="school" style="display:none">
<label for="fname">Grade:</label>
<input type="text" id="grade" name="Grade"><br><br>
<div id="students">
<div id="student">
<label for="fname">First:</label>
<input type="text" id="first" name="First"><br><br>
<label for="lname">Last:</label>
<input type="text" id="last" name="Last"><br><br>
</div>
<div class="text-center" id="add_student">
<span id="idStudentRootCopy">----S----</span>
<button type="button" onclick="addItem('student', 'idGradeRootCopy', false,this)">Student+</button>
</div>
</div>
</div>
<div class="text-center" id="add_grade">
<span id="idGradeRootCopy">----G----</span>
<button type="button" onclick="addItem('school', 'idGradeRootCopy', true,this)">Grade+</button>
</div>
</div>
Update
If you click on the Grade+ button, it will automatically also 'create' a student input field as it's div is part of the school div. So move it out of the school div and change it's display mode to none.
If you want the new student input field to appear right before the Student+ button, we indeed need to use .insertBefore().
Here's the modified example:
var count = 0;
function addItem(id, index, root, clickedElement) {
var original = document.getElementById(id);
var before = document.getElementById(index);
var clone = original.cloneNode(true);
clone.style.display = 'block';
clone.id = id + ++count;
var newFields = clone.childNodes;
for (var i = 0; i < newFields.length; i++) {
var fieldName = newFields[i].name;
if (fieldName)
newFields[i].name = fieldName + count;
}
if (root) {
original.parentNode.insertBefore(clone, before.parentNode);
} else {
clickedElement.parentNode.insertBefore(clone, clickedElement);
}
}
<div>
<label>Registration</label>
<div id="student" style="display:none">
<label for="fname">First:</label>
<input type="text" id="first" name="First"><br><br>
<label for="lname">Last:</label>
<input type="text" id="last" name="Last"><br><br>
</div>
<div class="form-inline justify-content-center" id="school" style="display:none">
<label for="fname">Grade:</label>
<input type="text" id="grade" name="Grade"><br><br>
<div id="students">
<div class="text-center" id="add_student">
<span id="idStudentRootCopy">----S----</span>
<button type="button" onclick="addItem('student', 'idStudentRootCopy', false,this)">Student+</button>
</div>
</div>
</div>
<div class="text-center" id="add_grade">
<span id="idGradeRootCopy">----G----</span>
<button type="button" onclick="addItem('school', 'idGradeRootCopy', true,this)">Grade+</button>
</div>
</div>
Html code
<div class="cont">
<div class="row">
<p>anything</p>
<input type="button" id="1" class="Done" name="Go" value="done">
</div>
<div class="row">
<p>anything</p>
<input type="button" id="2" class="Done" name="Go" value="done">
</div>
<div class="row">
<p>anything</p>
<input type="button" id="3" class="Done" name="Go" value="done">
</div>
</div>
I have 3 of them[buttons]
javascript
var remove=document.getElementsByClassName("Done")
for(var i=0;i<remove.length;i++){
var button=remove[i]
button.addEventListener('click',function(event){
var bclick = event.target
bclick.parentElement.remove()
});
}
I tried that, it's work for the first time but when I reload I miss changes.
I think you can use localstorage to track your removed parentElement. Simply check your localstorage whether your parentElement is removed or not, if it is removed already just hide your row class's elements. It will show nothing once button is clicked. Hope it will help.
var remove = document.getElementsByClassName("Done");
for (var i = 0; i < remove.length; i++) {
var button = remove[i];
if (button) {
if (window.localStorage.getItem(remove[i].id) == 'true') {
document.getElementById(remove[i].id).parentNode.style.display = 'none';
}
}
}
for (var i = 0; i < remove.length; i++) {
var button = remove[i];
if (button) {
button.addEventListener('click', (event) => {
var bclick = event.target;
window.localStorage.setItem(bclick.id, 'true');
bclick.parentElement.remove();
});
}
}
trying to create a dynamic button system to add/remove inputs on clicks. I have the addButton working but not the deleteButton. What am I missing?
$(document).ready(function() {
var maxFields = 20;
var addButton = $('#plusOne');
var deleteButton = $('#minusOne');
var wrapper = $('#userNumbers');
var fieldInput = '<div><input type="text" name="persons" id="persons"/></div>';
var x = 1;
$(addButton).click(function () {
if (x < maxFields) {
x++;
$(wrapper).append(fieldInput);
}
});
$(deleteButton).click(function(e) {
e.preventDefault();
$(this).parent('div').remove();
x--;
});
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<button type="button" id="plusOne">+</button>
<button type="button" id="minusOne">-</button>
<div id="userNumbers">
<p>
<input type="text" id="person" name="person">
</p>
</div>
The problem is, that $(this) inside the delete button handler refers to the minus button. That minus button is not inside each of the items (It's at the top. and doesn't have a parent div), so you need to reference the element you want to delete another way. In my case below, I'm selecting the last <div> in $(wrapper):
$(document).ready(function() {
var maxFields = 20;
var addButton = $('#plusOne');
var deleteButton = $('#minusOne');
var wrapper = $('#userNumbers');
var fieldInput = '<div><input type="text" name="persons" id="persons"/></div>';
var x = 1;
$(addButton).click(function () {
if (x < maxFields) {
x++;
$(wrapper).append(fieldInput);
}
});
$(deleteButton).click(function(e) {
e.preventDefault();
$(wrapper).find('div:last').remove();
x--;
});
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<button type="button" id="plusOne">+</button>
<button type="button" id="minusOne">-</button>
<div id="userNumbers">
<p>
<input type="text" id="person" name="person">
</p>
</div>
Also you can do it with pure JS using child Nodes. :D
Sometimes pure JS is beater than JQ
Explanation:
remove is a new function. It's a shortcut, making it simpler to remove an element without having to look for the parent node. It's unfortunately not available on old versions of Internet Explorer so, unless you don't want to support this browser, you'll have to use removeChild.
$(document).ready(function() {
var maxFields = 20;
var addButton = $('#plusOne');
var deleteButton = $('#minusOne');
var wrapper = $('#userNumbers');
var fieldInput = '<div><input type="text" name="persons" id="persons"/></div>';
var x = 1;
$(addButton).click(function () {
if (x < maxFields) {
x++;
$(wrapper).append(fieldInput);
}
});
$(deleteButton).click(function(e) {
e.preventDefault();
var myNode = document.getElementById("userNumbers");
i=myNode.childNodes.length - 1;
if(i>=0){
myNode.removeChild(myNode.childNodes[i]);
x--;
}
});
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<button type="button" id="plusOne">+</button>
<button type="button" id="minusOne">-</button>
<div id="userNumbers">
<p>
<input type="text" id="person" name="person">
</p>
</div>
Try below solution, I care about the back end also because you have to send the data to back end developer so you have to give the array name for input fields such as name="person[]". anyway you can try this solution also.
$(document).ready(function(){
var static_html = '<input type="text" name="person[]" class="input_fields" />';
$("#plus").click(function(){
if($(".input_fields").length < 20 )
$("#dynamic_field_container").append(static_html);
});
$("#minus").click(function(){
if($(".input_fields").length > 1 )
$(".input_fields:last").remove();
else
alert("This is default field so u can't delete");
});
});
.input_fields{
display:block;
}
<link href="https://stackpath.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet"/>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<span class="fa fa-plus fa-4x" id="plus"> </span>
<span class="fa fa-minus fa-4x" id="minus"> </span>
<div id="dynamic_field_container">
<input type="text" name="person[]" class="input_fields" />
</div>
I created below form: when you enter a name in first text box, it dynamically adds the names to another field below after pressing the + button. The function is implemented on the + button.
Now I want to add a validation logic within the same script, so that same name shouldn't be added twice. Please advise, only want to implement using javascript.
function promptAdd(list){
var text = "";
var inputs = document.querySelectorAll("input[type=text]");
for (var i = 0; i < inputs.length; i++) {
text += inputs[i].value;
}
var li = document.createElement("li");
var node = document.createTextNode(text);
li.appendChild(node);
document.getElementById("list").appendChild(li);
}
<!doctype html>
<html>
<div class="row">
<div class="col-lg-6 mb-1">
<div class="card h-100 text-left">
<div class="card-body">
<h4 class="card-title">Add Resources</h4>
<input type="text" class="form-control" name="employee" placeholder="Enter Name" />
<small id="message" class="form-text text-muted">Press + to add to your list</small>
<button id="bd1" class="btn add-more" onclick="promptAdd(list)" type="button">+</button>
<br></br>
<h5>List of Resources added</h5>
<div class="form-control" id="list">
<span id="list">
</div>
</div>
</div>
</div>
</div>
</html>
The validation could be implemented simply by looping through all the li's and comparing the text of every li with the value of the input and if the values matches just return false, like :
var lis = document.querySelectorAll('#list li');
for (var i = 0; i < lis.length; i++) {
if (lis[i].innerText == text) {
return false;
}
}
Hope this helps.
function promptAdd(list) {
var text = "";
var inputs = document.querySelectorAll("input[type=text]");
for (var i = 0; i < inputs.length; i++) {
text += inputs[i].value;
}
var lis = document.querySelectorAll('#list li');
for (var i = 0; i < lis.length; i++) {
if (lis[i].innerText == text ){
resetInputs();
return false;
}
}
var li = document.createElement("li");
var node = document.createTextNode(text);
li.appendChild(node);
document.getElementById("list").appendChild(li);
resetInputs();
}
function resetInputs(){
var inputs = document.querySelectorAll("input[type=text]");
for (var i = 0; i < inputs.length; i++) {
inputs[i].value = "";
}
}
<div class="row">
<div class="col-lg-6 mb-1">
<div class="card h-100 text-left">
<div class="card-body">
<h4 class="card-title">Add Resources</h4>
<input type="text" class="form-control" name="employee" placeholder="Enter Name" />
<small id="message" class="form-text text-muted">Press + to add to your list</small>
<button id="bd1" class="btn add-more" onclick="promptAdd(list)" type="button">+</button>
<br><br>
<h5>List of Resources added</h5>
<div class="form-control" id="list">
<span id="list"></span>
</div>
</div>
</div>
</div>
</div>
Loop though all li elements and check their innerText with the new text.
If you want to ignore capitalization you can use innerText.toUpperCase() === newText.toUpperCase()
function promptAdd(list) {
var text = "";
var inputs = document.querySelectorAll("input[type=text]");
for (var i = 0; i < inputs.length; i++) {
text += inputs[i].value;
}
if (textAlreadyExistsInList(text)) {
return;
};
var li = document.createElement("li");
var node = document.createTextNode(text);
li.appendChild(node);
document.getElementById("list").appendChild(li);
};
function textAlreadyExistsInList(text) {
var itemExists = false;
var items = document.getElementById("list").querySelectorAll('li');
for (var i = 0; i < items.length; i++) {
if (items[i].innerText === text) { //to ignore casing: items[i].innerText.toUpperCase() === text.toUpperCase()
itemExists = true;
break;
}
}
return itemExists;
}
<div class="row">
<div class="col-lg-6 mb-1">
<div class="card h-100 text-left">
<div class="card-body">
<h4 class="card-title">Add Resources</h4>
<input type="text" class="form-control" name="employee" placeholder="Enter Name" />
<small id="message" class="form-text text-muted">Press + to add to your list</small>
<button id="bd1" class="btn add-more" onclick="promptAdd(list)" type="button">+</button>
<br></br>
<h5>List of Resources added</h5>
<div class="form-control" id="list">
</div>
You need one input text so given that id is better . Here I set insert_name as id ! Get all li by querySelectAll and check text with innerHTML and input value .
function promptAdd(list){
var inputs = document.getElementById("insert_name").value;
if(checkDuplicate(inputs)) return; // check duplicate
var li = document.createElement("li");
var node = document.createTextNode(inputs);
li.appendChild(node);
document.getElementById("list").appendChild(li);
}
function checkDuplicate(name) {
var flag = false;
var lis = document.querySelectorAll("li");
for(var i = 0 ;i < lis.length;i++) {
if(name == lis[i].innerHTML) {
flag = true;
break;
}
}
return flag;
}
I have a form that user can select a/some year form select-options list than add these to next select-list.
To achieve it, I've create 2 select-option and four buttons to add or remove.
The part to insert and remove are achieved but the problem appear when user click add button but there is/arenn't options selected.
The error-message is successful appeared with add jquery animate-fadein/out.
But when error-message have appeared, the button isn't accessible/cann't clicked.
Below is the HTML, and js.
HTML
<div id="segitiga" class="sgtg1" style="background-image: url('picture/left_triangle.png'); display: none"></div>
<div id="err_msg_border" class="err_msg_border1" style="display: none"></div>
<div id="err_msg" class="err_msg1" style="display: none;">Pilih satu atau lebih tahun untuk dimasukkan ke daftar sebelah kanan</div>
<select id="list_tahun" style="width: 80px; margin-right: 5px" multiple="multiple" size="22"></select>
<input type="button" value="Add > >" class="btn_tahun" id="A" >
<input type="button" value="Add All > >" class="btn_tahun" id="AA">
<input type="button" value="< < Remove" class="btn_tahun" id="R" disabled="disabled" >
<input type="button" value=" < < Remove All" class="btn_tahun" id="RA" disabled="disabled">
<div id="segitiga" class="sgtg2" style="background-image: url('picture/right_triangle.png');display: none"></div>
<div id="err_msg_border" class="err_msg_border2" style="display:none"></div>
<div id="err_msg" class="err_msg2" style="display: none">Pilih satu atau lebih tahun yang akan dihapus</div>
Javascript
$(document).ready(function() {
a = document.getElementById('list_tahun');
b = document.getElementById('list_tahun_pilihan');
$("#A").click(function() {
count = 0;
count2 = 0;
for (var i = 0; i < a.options.length; i++) {
if (a.options[i].selected) {
option = document.createElement("option");
option.text = a.options[i].text;
option.value = a.options[i].value;
b.add(option);
a.options[i].selected = false;
a.options[i].disabled = true;
count++;
}
}
if (count < 1) {
$(".sgtg1").fadeIn();
$(".err_msg_border1").fadeIn();
$(".err_msg1").fadeIn();
} else if (count > 0) {
$(".sgtg1").fadeOut();
$(".err_msg_border1").fadeOut();
$(".err_msg1").fadeOut();
document.getElementById('R').disabled = false;
document.getElementById('RA').disabled = false;
}
for (var i = 0; i < a.options.length; i++) {
if (a.options[i].disabled) {
count2++;
}
}
if (count2 === a.options.length) {
document.getElementById('A').disabled = true;
document.getElementById('AA').disabled = true;
} else {
document.getElementById('A').disabled = false;
}
});
....
How I can set up the focus again to the buttons ?
-Thanks-
Your html is invalid.
First Change this:
<input type="button" value="Add>>" class="btn_tahun" id="A" >
To:
<input type="button" value="Add>>" class="btn_tahun" id="A" >
Text like < or > should be written in code like this:
< ==> <
> ==> >
After I inspect element with firefox, the height of area of error message -class="err_msg1" is too height and it's covered the buttons. So, when I want to click the button, the cursor will appear into class="err_msg1".
My advice, be crefully about specify each height-width of element so, when you want some of element runs well, don't let their layer are stacked.