error on removing divs dynamically - javascript

I'm trying to add 3 fields dynamically (2 text fields and 1 remove button) per row. The add button is working fine. The problem is the remove function. When I add 2 fields or more, and I try to remove the first, both added fields/rows are deleted
This is my html code:
<div class="col-lg-12">
<div class="field_wrapper form-group col-sm-12">
<div class="form-group col-sm-4">
<input type="text" name="id[]" value="" class="form-control"/>
</div>
<div class="form-group col-sm-4">
<input type="text" name="name[]" value="" class="form-control"/>
</div>
<div class="form-group col-sm-4">
Adicionar Valor
</div>
</div>
</div>
This is the javascript:
<script type="text/javascript">
$(document).ready(function(){
var maxField = 10; //Input fields increment limitation
var addButton = $('.add_button'); //Add button selector
var wrapper = $('.field_wrapper:last'); //Input field wrapper
var wrapper_delete = $('.field_wrapper');
//var fieldHTML = '<div><input type="text" name="field_name[]" value=""/>Remover</div>'; //New input field html
var fieldHTML = '<div class="field_wrapper form-group col-sm-12"><div class="form-group col-sm-4"><input type="text" name="id[]" value="" class="form-control"/></div><div class="form-group col-sm-4"><input type="text" name="name[]" value="" class="form-control"/></div><div class="form-group col-sm-4"><i class="fa fa-trash-o"></i></div></div>'; //New input field html
var x = 1; //Initial field counter is 1
$(addButton).click(function(){ //Once add button is clicked
wrapper = $('.field_wrapper:last'); //Input field wrapper
if(x < maxField){ //Check maximum number of input fields
x++; //Increment field counter
var count = $('.field_wrapper').length;
fieldHTML = '<div class="field_wrapper form-group col-sm-12"><div class="form-group col-sm-4"><input type="text" name="id_'+ count +'" value="" class="form-control"/></div><div class="form-group col-sm-4"><input type="text" name="name_' + count +'" value="" class="form-control"/></div><div class="form-group col-sm-4"><i class="fa fa-trash-o"></i></div></div>'; //New input field html
$(wrapper).append(fieldHTML); // Add field html
}
});
$(wrapper_delete).on('click', '.remove_button', function(e){ //Once remove button is clicked
wrapper_delete = $('.field_wrapper');
e.preventDefault();
$(this).closest('.field_wrapper').remove();
x--; //Decrement field counter
});
});
</script>
How can I fix this?

You're appending the new field_wrapper element to field_wrapper:last. You should place it after, so instead of .append() you should use .after().
$(wrapper).after(fieldHTML); // Add field html
And delegate click event on document (or closest static ancestor), because .field_wrapper element is also dynamicaly added and you can't attach event to it.
$(document).on('click', '.remove_button', function(e){
JSFiddle
Looking at what you're trying to do. I'd recommend to use .clone([with events]). You don't actually need to change input names (unless the reason is other than avoiding name duplicates), as they are set to arrays.
This way, all your code could be as simple as below:
$('.add_button').click(function(){
$('.field_wrapper').length >= maxField ||
wrapper.clone(true).find('a.btn').toggle().end().insertAfter('.field_wrapper:last');
});
$('.remove_button').hide().click(function(){
$(this).closest('.field_wrapper').remove();
});
var maxField = 10, wrapper = $('.field_wrapper').clone(true);
JSFiddle

Related

How to Remove a Div using variable parameters?

I'm trying to create a way to add entries to my form so that the user can choose how many he wants, but I can't get the remove part working.
JavaScript
var i = 1;
var divContent = document.getElementById('formulario');
//Click to add a field
function cria() {
//This add a HTML Inputs and divs who the ID is variable how the 'i' is increasedf
document.getElementById('formulario').innerHTML += '<div class="mb-1 col-3" id="div'+i+'"><label for="nomeTx0" class="form-label">Nome</label><input type="text" class="form-control" id="nomeTx'+i+'" name="nomeTx'+i+'" required></div><div class="mb-1 col-3" id="div2'+i+'"><label for="taxa'+i+'" class="form-label">Valor</label><input type="float" class="form-control" id="taxa'+i+'" name="taxa'+i+'" required></div>- Remover campo';
i++;
}
function remove(div1, div2){
var div = document.getElementById(div1);
var div2 = document.getElementById(div2);
div.remove();
div2.remove();
i--;
}
And now the HTML
<form>
<h4 class="card-tittle text-center">Taxas</h4>
<div id="formulario" class="form row align-items-start">
<div class="mb-1 col-3" id="0">
<label for="nomeTx0" class="form-label">Nome</label>
<input type="text" class="form-control" id="nomeTx0" name="nomeTx0" required>
</div>
<div class="mb-1 col-3" id="0">
<label for="taxa0" class="form-label">Valor</label>
<input type="float" class="form-control" id="taxa0" name="taxa0" required>
</div>
</div>
+ adicionar campo
<div class="mb-1 col-lg-12" style="text-align: center;">
<button class="btn btn-primary col-5" id="Enviar" type="submit" text="Enviar">Adicionar Taxas</button>
</div>
</form>
ID="taxa"+i but when I call the remove(); error is printed to me saing the variable is null.
This is really not the right approach in the first place.
Your fundamental problem is that you are relying on ids to know what element(s) to add and remove and this is leading you to concatenate an id onto dynamically created elements, made from long strings with variables concatenated into them. In reality, you should avoid ids whenever possible as they make your code very brittle and don't scale well.
This is a perfect use for the HTML <template> element. As you can see from the re-worked code below, all ids have been removed - - you don't need them. Additionally, instead of long strings with a variable concatenated into it, you just need to copy/clone the template whenever you need one. Then, you can use "event delegation" and smartly organized HTML to just set up a single click event on a master wrapper element, where the actual element that was clicked (the event.target) can be checked. If it was a remove button, then just remove the entire wrapper that is the nearest ancestor to the remove button that was clicked.
You can now add and remove as many items as you like with no need for an id or counting variables!
// Get a reference to the template, outer div and the add "button"
const template = document.querySelector("template");
const wrapper = document.querySelector(".wrapper");
const add = document.querySelector(".add");
// Set up the add event in Javascript, not with inline HTML
add.addEventListener("click", function(event){
var clone = template.content.cloneNode(true); // Clone the template
wrapper.appendChild(clone);
});
// Set up a wrapper level click event that any clicks within it will bubble up to
wrapper.addEventListener("click", function(event){
// Test to see if it was remove "button" that was clicked
if(event.target.classList.contains("remove")){
// Just remove the closest ancestor div that holds that particular group
// and remove it.
event.target.closest("div.templateWrapper").remove();
}
});
.mb-1.col-lg-12 {
text-align:center;
}
.mb-1.col-3 {
margin:2px;
}
.add, .remove {
cursor:pointer;
color:blue;
}
.labelName { display:inline-block; width:3em; }
/* This is just to better see the groups */
.templateWrapper, .form {
background-color:aliceblue;
padding:5px;
margin:8px;
}
<!-- This will not initially be shown on the page.
It will be used to copy from when/if needed. -->
<template>
<div class="templateWrapper">
<div class="mb-1 col-3">
<label class="form-label"><span class="labelName">Nome</span>
<input type="text" class="form-control" name="nomeTx" required>
</label>
</div>
<div class="mb-1 col-3">
<label class="form-label"><span class="labelName">Valor</span>
<!-- An input does not have a type=float -->
<input class="form-control" name="taxa" required>
</label>
</div>
<span class="remove">- Remover campo</span>
</div>
</template>
<form>
<h4 class="card-tittle text-center">Taxas</h4>
<!-- Hyperlinks are for navigation, not JavaScript click hooks. Any visible element
supports a click event. Use span and div for generic clickable inline or block
elements that need to have click event handlers. -->
<span class="add">+ adicionar campo</span>
<div class="wrapper">
<div class="form row align-items-start">
<div class="mb-1 col-3">
<label class="form-label"><span class="labelName">Nome</span>
<input type="text" class="form-control" name="nomeTx0" required>
</label>
</div>
<div class="mb-1 col-3">
<label class="form-label"><span class="labelName">Valor</span>
<input type="float" class="form-control" name="taxa0" required>
</label>
</div>
</div>
</div>
<div class="mb-1 col-lg-12">
<!-- A button does not have a "text" attribute -->
<button class="btn btn-primary col-5" type="submit">Adicionar Taxas</button>
</div>
</form>
You would like to pass the ids of the html elements to function remove , instead you pass something else.
Try this:
function remove(d1, d2){
//what are passing to function... id , or something else ?
console.log(d1,d2);
// now I force the arguments passed to function to a valid value id for test
var a = document.getElementById('div1'); // id is div1
var b = document.getElementById('div21'); // id is div21
//Ask to parentNode to remove his child
a.parentNode.removeChild(a);
b.parentNode.removeChild(b);
i--;
}
The problem in your code is that you don't pass a string to the remove function but instead you pass the whole element. That is why document.getElementById can't find anything because it expects a string as a parameter. I refactored you code a bit and also when removing the fields the link - Remover campo stayed and was not deleted. I fixed that as well by passing a third argument to the remove function.
var i = 1;
var divContent = document.getElementById('formulario');
//Click to add a field
function cria() {
//This add a HTML Inputs and divs who the ID is variable how the 'i' is increasedf
document.getElementById('formulario').innerHTML += '<div class="mb-1 col-3" id="div'+i+'"><label for="nomeTx0" class="form-label">Nome</label><input type="text" class="form-control" id="nomeTx'+i+'" name="nomeTx'+i+'" required></div><div class="mb-1 col-3" id="div2'+i+'"><label for="taxa'+i+'" class="form-label">Valor</label><input type="float" class="form-control" id="taxa'+i+'" name="taxa'+i+'" required></div>- Remover campo';
i++;
}
function remove(div1, div2, link){
var div = document.getElementById(div1);
var div2 = document.getElementById(div2);
var link = document.getElementById(link);
divContent.removeChild(div);
divContent.removeChild(div2);
divContent.removeChild(link)
i--;
}
The easiest way to do your code working it change your function "cria". (it's not the best option)
You miss ' '.
You have this.
onclick="remove(div'+i+',div2'+i+')"
You need this.
onclick="remove(\'div'+i+'\',\'div2'+i+'\')"
Javascript just doesn't understand that these parameters are strings.
And the full function "cria" after changes.
function cria() {
//This add a HTML Inputs and divs who the ID is variable how the 'i' is increasedf
document.getElementById('formulario').innerHTML += '<div class="mb-1 col-3" id="div'+i+'"><label for="nomeTx0" class="form-label">Nome</label><input type="text" class="form-control" id="nomeTx'+i+'" name="nomeTx'+i+'" required></div><div class="mb-1 col-3" id="div2'+i+'"><label for="taxa'+i+'" class="form-label">Valor</label><input type="float" class="form-control" id="taxa'+i+'" name="taxa'+i+'" required></div>- Remover campo';
i++;
}

Use maskMoney in input that is created dynamic

In the form that opens by default the function in js works well.
But then in the form I have the possibility to create more fields dynamically, but in those fields that you create dynamically it doesn't work.
Let me explain.
Code:
$(document).ready(function() {
var max_fields = 10; //maximum input boxes allowed
var wrapper = $(".input_fields_wrap"); //Fields wrapper
var add_button = $(".add_field_button"); //Add button ID
var x = 1; //initlal text box count
$(add_button).click(function(e) { //on add input button click
e.preventDefault();
var length = wrapper.find("input:text").length;
if (x < max_fields) { //max input box allowed
x++; //text box increment
$(wrapper).append('<div id="teste"><div class="form-group col-md-2"><input type="text" class="form-control1 Preco" name="Valor[]" value="0.00" /><span class="form-highlight">$</span><span class="form-bar"></span><label class="label1" for="Valor">Valor</label></div><button class="remove_field" style="background-color: #313348;"><span class="fa fa-trash fa-fw" aria-hidden="true"></span></button></div>');
}
});
$(wrapper).on("click", ".remove_field", function(e) { //user click on remove text
e.preventDefault();
$("#teste").remove();
x--;
})
});
$(function() {
$('.Preco').maskMoney({ decimal: '.', thousands: ' ', precision: 2 });
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-maskmoney/3.0.2/jquery.maskMoney.min.js"></script>
<button class="btn btn-warning caixa add_field_button" style="float:right;"><i class="fa fa-plus-square fa-5x taman" aria-hidden="true"></i></button>
<div class="input_fields_wrap">
<div class="form-group col-md-2">
<input type="text" class="form-control1 Preco" name="Valor[]" value="0.00" required>
<span class="form-highlight">$</span>
<span class="form-bar"></span>
<label class="label1" for="Valor">Valor</label>
</div>
</div>
As I show in the example the first input works correctly, but when I create a dynamic input it lets the maskMoney function work
$(document).ready(function() { and $(function() { are the same thing. (This is mentioned in the jQuery documentation for .ready()) Because of that, you've essentially declared two onready handlers. onready handlers can happen in any order.
Combine them into one onready handler and order the contents such that the call to maskMoney happens last.

When i click on add more button the new datepicker field is added the previous datepicker value is reset

The problem is ,when i click the "Add Button" new datepicker field is added which display the current date as default and when i change manually the date and click on add button then it will reset the default value in all datepicker.
MY CODE IS BELOW:
<div class="form-group row">
<label for="example-date-input" class="col-2 col-form-label" style="margin-left:28.5%;">DATE</label>
<div class="col-10">
<input class="form-control datepicker pick" id="date[]" name="date[]" value="<?php echo $_POST['date'] ?>" style="" type="text" readonly>
</div>
</div>
<div class="form-group">
<div class="col-md-8 col-sm-12 col-24">
<div class="input_fields" style="color:black">
<button class="add_field btn " onclick="incrementValue()" style="">Add More</button>
<div>
<input type="text" name="mytextt[]" hidden="" ></div>
</div>
</div>
</div>
javascript:
$(document).ready(function() {
var max_fields = 10; //maximum input boxes allowed
var wrapper = $(".input_fields"); //Fields wrapper
var add_button = $(".add_field"); //Add button ID
var wrapper_pre1 = $(".present_fields_1"); //Fields wrapper
var x = 1; //initlal text box count
$(add_button).click(function(e){ //on add input button click
e.preventDefault();
if(x < max_fields){ //max input box allowed
x++; //text box increment
$(wrapper).prepend('<div class="form-group row"><label for="example-date-input" class="col-2 col-form-label date">DATE</label><div class="col-10 col"><input class="form-control datepicker pick" id="date[]" name="date[]" style="" type="text"></div></div>');$( ".datepicker.pick" ).datepick({dateFormat: 'dd/mm/yyyy'}).datepick("setDate", "0");}});});
so for your requirement create unique ID for each field (input box) by attaching the count or something variable to the ID. and create your JS also with ID not using class. SO that will nto affect other input box.
Something like this, see ID of the box and date picker method too
$(wrapper).prepend('<div class="form-group row"><label for="example-date-input" class="col-2 col-form-label date">DATE</label><div class="col-10 col"><input class="form-control datepicker pick" id="date_'+x+'" name="date[]" style="" type="text"></div></div>');$( "#date_"+x ).datepick({dateFormat: 'dd/mm/yyyy'}).datepick("setDate", "0");}});});
Please notice ID implementation.
Important: Please look at into the difference between using class and ID.
Difference between class and ID
JQuery Selectors-w3school
How do I select an item using class or ID?

Hiding and showing div series using Javascript

I'm making basic GPA calculator using Javascript.
Here is my code:
<div class="list">
<div class="row">
<div class="col col-50">Subject 1</div>
<div class="col"><input type="text" name ="GR1" placeholder="Grade"></div>
<div class="col"><input type="tel" name="CR1" placeholder="Credits"></div>
</div>
<div class="row">
<div class="col col-50">Subject 2</div>
<div class="col"><input type="text" name ="GR2" placeholder="Grade"></div>
<div class="col"><input type="tel" name ="CR2" placeholder="Credits"></div>
</div>
<button class="button button-positive">
Add Another Field //it can add uptop 10 fields
</button>
</div>
It will increment the same div series while incrementing the input name up to 10 fields. User can click Add Another Field and add a new div field.
In every div field, it only changes the subject and input fields' name with an incrementation of 1.
Question:
What is the best way to achieve this without duplicating the same thing over and over? Or do I need to first create 10 div forms and hide all and show them one by one upon each click? Please give me example.
Here is a solution that is in pure Javascript that will allow you to add up to 10 "field blocks". In the HTML file, put:
<div id="list">
<button onclick="addRow()">Add another field</button>
</div>
And here's the Javascript function to add a new row, and initialise the two first row:
<script type="text/javascript">
window.onload = function() {
addRow();
addRow();
};
function addRow() {
var element = document.getElementById('list');
var nextId = element.childElementCount;
if (nextId <= 10) {
var div = document.createElement('div');
div.setAttribute('class', 'row');
div.innerHTML = '<div class="col col-50">Subject ' + nextId + '</div><div class="col"><input type="text" name ="GR' + nextId + '" placeholder="Grade"></div><div class="col"><input type="tel" name="CR' + nextId + '" placeholder="Credits"></div>';
element.insertBefore(div, element.getElementsByTagName('button')[0]);
}
}
</script>
You can try it online on the following fiddle: https://jsfiddle.net/w82t30r4/
Try jQuery's clone (read about it here)
$(document).ready(function(){
$row = $(".row").clone();
$("button").click(function(){
$(".list").append($row.clone());
})
})
What's happening is that I clone the row to start with (before any data is in it). Then I add a clone of that clone to .list when the button is clicked.

replace a <p> element with an input element

I have a p element which I want to replace with an input element when user clicks on it. The might not be present during page load so I use delegate to catch the click event.
html(after <p> was loaded)
<div class="phrases">
<div class="predefined-phrase>
<p id="1">A predefined phrase"</p>
</div>
</div>
I want it to be like this
<div class="phrases">
<div class="predefined-phrase">
<input type="text" id="1" class="form-control input-sm" value="A predefined phrase">
</div>
</div>
My js file has the following code:
$(".phrases").on('click', ".predefined-phrase", function (event){
event.preventDefault();
var phrase = $(this).children().text();
var id = $(this).children().attr('id');
var input = '<input type="text" class="form-control input-sm" id="'+id+'" value="'+phrase+'">';
$(this).children().remove();
$(this).append(input);
});
<p> is replaced normally by input but i cannot type anything. I can only type if left click is pressed on input box continiously. I also want to catch a keypress event on the new input so to edit or to delete the specific phrase. But I cannot even type on the input box. Why is this happening? Can i normally catch the keypress event after I have appended the input (and works as it should) inside the click event callback? The point is that after user presses the phrase is edited with ajax and the input box dissappears and p is loaded back with the new edited phrase or fully deleted.
$(function(){
$('.predefined-phrase p').click(function() {
var id = $(this).attr('id');
var phrase = $(this).text();
var newInput="<input type='text' id='"+id+"' value='"+phrase+"' />";
$(this).replaceWith(newInput);
});
});
DEMO FIDDLE
Just check the target. If it is a input, do nothing :
$(".phrases").on('click', ".predefined-phrase", function (event){
if($(event.target).is('input')) return;
event.preventDefault();
var phrase = $(this).children().text();
var id = $(this).children().attr('id');
var input = '<input type="text" class="form-control input-sm" id="'+id+'" value="'+phrase+'">';
$(this).children().remove();
$(this).append(input);
});
Well, this is a native JS solution, but hopefully it will point you in the right direction, or if you an use it instead of jQuery, that works too. Not that I've changed your ID to not start with a number, as mentioned by C-link, as that is not allowed.
document.getElementById("n1").addEventListener("click", function filler(){
this.outerHTML = '<input type="text" id="' + this.id + '" class="form-control input-sm" value="' + this.innerHTML + '" />
this.removeEventListener("click" filler)'
});
I would suggest the next changes:
<div class="predefined-phrase">
<input type="text" data-id="1" class="form-control input-sm" value="A predefined phrase">
</div>
jQuery script:
$('.predefined-phrase').click(function() {
var id = $('p', this).attr('data-id');
var value = $('p', this).text();
$('p', this).replaceWith('<input type="text" data-id="' + id + '" class="form-control input-sm" value="' + value + '" />')
});
First of all, don't use id starting from number.
And for your solution you can use replaceWith method like below:
$('#your_id').replaceWith('<input type="text" id="your_id" class="form-control input-sm" value="A predefined phrase" />');

Categories