Adding div won't work after I remove a div. Why? - javascript

I'm making an expense tracker with HTML, CSS, and JavaScript and I ran into a problem where when I first load up the page I can add as many div items I want, but then after I delete one (or more) of them, the adding button doesn't work anymore. The error I get in the console is:
main.js:8 Uncaught TypeError: Cannot read properties of null (reading 'appendChild') at HTMLButtonElement.duplicate (main.js:8:25).
I have a function for removing a div (in this case the div is the expense item in the list), called deleteItem(), and another one for adding a div, called duplicate(). I'm not sure what is happening, but if anyone can help, that would be great.
I've seen a few possible solutions on YouTube that use JQuery, but since I'm still learning JavaScript, I'm trying not to get into that just yet. However, if the solution requires JQuery or anything like that, let me know.
document.getElementById('new-item-btn').onclick = duplicate;
var original = document.getElementById('expense-item');
function duplicate() {
var clone = original.cloneNode(true);
clone.id = "expense-item";
original.parentNode.appendChild(clone);
}
function deleteItem() {
var removeItem = document.getElementById('expense-item');
removeItem.remove();
}
<main id="main">
<div class="container" id="container">
<div class="expense-item" id="expense-item">
<button class="delete-btn" onclick="deleteItem()">Delete</button>
<div class="expense-inputs">
<label for="expense-name"></label>
<input type="text" placeholder="Expense Name" id="expense-name">
<label for="expense-category"></label>
<select name="expense-category" id="expense-category">
<option value="">Category</option>
<option value="groceries">Groceries</option>
<option value="housing">Housing</option>
<option value="utilities">Utilities</option>
</select>
<label for="expense-amount"></label>
<input type="text" id="expense-amount" name="expense-amount" placeholder="Amount">
</div>
</div>
</div>
<div class="info">
<div class="new-item">
<button class="new-item-btn" id="new-item-btn" onclick="duplicate()">New Item</button>
</div>
</div>
</main>

What you can do to make the delete method dynamic and work without id is:
function deleteItem(obj) { // <- obj refers to the clicked object because of `this` in onclick="deleteItem()"
var removeItem = obj.parentElement; // <- use parentElement to target the "<div class="expense-item" id="expense-item">"
removeItem.remove();
}
Then you just need to add this to onclick="deleteItem()"
Note I would advice you to still change the id to something new on the create but this is the example of the delete method.
demo
document.getElementById('new-item-btn').onclick = duplicate;
var original = document.getElementById('expense-item');
function duplicate() {
var clone = original.cloneNode(true);
original.parentNode.appendChild(clone);
}
function deleteItem(obj) {
var removeItem = obj.parentElement;
removeItem.remove();
}
<main id="main">
<div class="container" id="container">
<div class="expense-item" id="expense-item">
<button class="delete-btn" onclick="deleteItem(this)">Delete</button>
<div class="expense-inputs">
<label for="expense-name"></label>
<input type="text" placeholder="Expense Name" id="expense-name">
<label for="expense-category"></label>
<select name="expense-category" id="expense-category">
<option value="">Category</option>
<option value="groceries">Groceries</option>
<option value="housing">Housing</option>
<option value="utilities">Utilities</option>
</select>
<label for="expense-amount"></label>
<input type="text" id="expense-amount" name="expense-amount" placeholder="Amount">
</div>
</div>
</div>
<div class="info">
<div class="new-item">
<button class="new-item-btn" id="new-item-btn" onclick="duplicate()">New Item</button>
</div>
</div>
</main>

You can, in most instances, remove IDs when creating dynamic content and use other means of identifying DOM nodes - such as querySelector in combination with event.target. Inspecting an Event to determine the origin of the click is probably the most reliable method.
The dynamically added content does not know in advance IDs or other attributes so by supplying the event as an argument to the deleteItem method you can identify the newly added content from button to container and remove it quite easily.
document.querySelector('.new-item-btn').onclick = duplicate;
function duplicate() {
const original = document.querySelector('[data-id="expense-item"]');
original.parentNode.appendChild( original.cloneNode(true) );
}
function deleteItem(e) {
let item = e.target.closest('.expense-item');
if( document.querySelectorAll('.expense-item').length > 1 ){
item.parentNode.removeChild( item );
}
}
<main>
<div class="container">
<div class="expense-item" data-id="expense-item">
<button class="delete-btn" onclick="deleteItem(event)">Delete</button>
<div class="expense-inputs">
<label>
<input type="text" placeholder="Expense Name" />
</label>
<label>
<select name="expense-category" data-id="expense-category">
<option value="">Category</option>
<option value="groceries">Groceries</option>
<option value="housing">Housing</option>
<option value="utilities">Utilities</option>
</select>
</label>
<label>
<input type="text" name="expense-amount" placeholder="Amount" />
</label>
</div>
</div>
</div>
<div class="info">
<div class="new-item">
<button class="new-item-btn" data-id="new-item-btn" onclick="duplicate()">New Item</button>
</div>
</div>
</main>

Related

How can I pass the value from drop down list as a link for button?

I am creating a web app (using thymeleaf, html, css, javascript) and I ran into a problem. Say I have a search bar and a button like this:
Now this represents the functionality of an app and currently it can only search records from a table by their name. But I want to add some other functions like "search by population", "search by capital" etc. I was planning on creating a drop-down list next to the search bar where these options will be included, and the user will select from that list how he wants to search for a record. I can't see a way to do that. Currently this is a search bar code:
<h4 class="left">
<form ui-jp="parsley" th:action="#{/events/showByState}" th:object="${myState}" method="get">
<div class="row m-b">
<div class="child">Search by State Name:</div>
<div class="child"><input id="filter" type="text" th:field="*{officialStateName}" class="form-control input-sm w-auto inline m-r"/></div>
<div class="child box-3">
<button class="btn">Search!</button>
</div>
</div>
</form>
</h4>
So it is unclear for me how I do this because I need to specify the link for button based on what user will choose from the list. Any help is appreciated!
You can create a <select> element with options whose value indicate what the action of the form should be. Whenever its value changes, you can update the form's action.
document.getElementById('search-by').addEventListener('change', function(e) {
let searchBy = this.value;
console.log(searchBy);
this.form.action = `/events/${searchBy}`;
});
<form ui-jp="parsley" th:action="#{/events/showByState}" th:object="${myState}" method="get">
<div class="row m-b">
<div class="child">Search by
<select id="search-by">
<option value="showByState">State Name</option>
<option value="showByPopulation">Population</option>
<option value="showByCapital">Capital</option>
</select>:</div>
<div class="child"><input id="filter" type="text" th:field="*{officialStateName}" class="form-control input-sm w-auto inline m-r" /></div>
<div class="child box-3">
<button class="btn">Search!</button>
</div>
</div>
</form>

Jquery Need siblings value but not working

I want to populate the value of the "eventTitle" in "Requirement" input box when some one click on the corresponding check box. i.e If some one clieck on the check box of Vels Group Of Instutions then automatically i want this to populate in texbox with name "Requirement" if multiple check box are clicked i want it to be comma seperated. Below is the code i tried to get but it is not working and getting undefined.
<div class="wid100">
<div class="eventTitle">Vels Group Of Instutions</div>
<div class="eventDate">2017-07-25</div>
<div class="eventVenue">This is world wide institute of technology </div>
<div class="selectEvent">
<input type="checkbox" class="seminar selected" id="179">
<label for="179"></label>
</div>
</div>
<div class="wid100">
<div class="eventTitle">Title goes here</div>
<div class="eventDate">2017-07-25</div>
<div class="eventVenue">sdfdsafasdfdsafdsafsadfsdfsdf </div>
<div class="selectEvent">
<input type="checkbox" class="seminar" id="179">
<label for="179"></label>
</div>
</div>
<input type="text" name="Requirement" placeholder="Title 01" id="divclass" required="required" class="pull-left" />
<script type="text/javascript" src="js/jquery-1.9.1.js"></script>
<script type="text/javascript" src="js/jquery-ui.js"></script>
$(".seminar").click(function () {
if ($(this).is(":checked")) {
//checked
$(this).addClass("selected");
var event_title = "";
event_title = $(".selected").siblings('.eventTitle').val();
console.log(event_title); return false;
} else {
//unchecked
$(this).removeClass("selected");
}
});
.eventTitle is not the sibling of .selected and the .eventTitle is a div element having no value, text there. change this line
event_title = $(".selected").siblings('.eventTitle').val();
to
event_title = $(this).parent().siblings('.eventTitle').text();
or
event_title = $(this).parent().siblings('.eventTitle').html();
The issue you have is because .eventTitle is not a sibling of the clicked checkbox, so the DOM traversal logic is wrong. div elements also do not have a val(), so you should use text() or html() instead.
However, you can improve the logic and also achieve the comma separated list of the selected event titles by using map() to build an array which you can then join() before setting in the value of #divclass. Try this:
$(".seminar").click(function() {
$(this).toggleClass('selected', this.checked);
var eventNames = $('.seminar:checked').map(function() {
return $(this).closest('.wid100').find('.eventTitle').text();
}).get().join(',');
$('#divclass').val(eventNames);
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<div class="wid100">
<div class="eventTitle">Vels Group Of Instutions</div>
<div class="eventDate">2017-07-25</div>
<div class="eventVenue">This is world wide institute of technology </div>
<div class="selectEvent">
<input type="checkbox" class="seminar selected" id="179">
<label for="179"></label>
</div>
</div>
<div class="wid100">
<div class="eventTitle">Title goes here</div>
<div class="eventDate">2017-07-25</div>
<div class="eventVenue">sdfdsafasdfdsafdsafsadfsdfsdf </div>
<div class="selectEvent">
<input type="checkbox" class="seminar" id="179">
<label for="179"></label>
</div>
</div>
<input type="text" name="Requirement" placeholder="Title 01" id="divclass" required="required" class="pull-left" size="100" />
I'd suggest changing the id of the #divclass to something more descriptive, as the element is not a div, and it's an identifier, not a class.
Finally, your .seminar elements have the same id attribute which is invalid. You should ensure that the ids are unique within the DOM - assuming that this is not just a typo from copy/pasting the code in the question.

I want to access discount prize but its not getting

In this code I want to calculate discount present.
There is one offer selection list if we select offer as yes then I shows <div id="dis11"> i.e. ($("#dis11").show();). There is jquery call. In that div four text boxes are there offerstart date, end date, discount prize, discount%. I want to take discount prize in jquery call, but I didn't get it.
adis=document.getElementById("discount").value;
that's the problem.
In this there is prize text box which contains original prize of product. Using that prize and discount prize I have to calculate discount persent. On prize text box I gave onblur event. Bez if prize change discount present also going to change.
<div class="formrow1">
<div class="txttitle_large1">Prize</div>
<div class="txtinputouter1">
<input type="text" name="prize" id="prize1" onblur="prizf();">
</div>
</div>
</div>
<div class="txttitle" style="width:30px;">Offer</div>
<div class="txtinputouter1">
<select class="required" name="offer" id="offers" onChange="discountajax();">
<option value="11">--Select offer--</option>
<option value="1">yes</option>
<option value="0">no</option>
</select>
<div id="discountinfo">
</div>
</div>
<div style="width:70%;height:auto;float:left;margin-top:10px; display:none;" id="dis11">
<div class="formrow1">
<div class="txttitle_large1">OfferStart</div>
<div class="txtinputouter1">
<input type="text" name="offerstart" id="offerstart"/>
</div>
</div>
<div class="formrow1">
<div class="txttitle_large1">OfferEnd</div>
<div class="txtinputouter1">
<input type="text" name="offerEnd" id="offerEnd"/>
</div>
</div>
<div class="formrow1">
<div class="txttitle_large1">Discount Prize</div>
<div class="txtinputouter1">
<input type="text" name="discountprize" id="discount"/>
</div>
</div>
<div class="formrow1">
<div class="txttitle_large1">Discount%</div>
<div class="txtinputouter1">
<input type="text" name="discountpercent" id="discountpercent"/>
</div>
</div>
</div>
--------script---------------
<script>
function prizf()
{
alert("hi");
discountajax();
}
</script>
<script>
function discountajax()
{
p=document.getElementById("prize1").value;
alert(p);
id1=document.getElementById("offers").value;
alert(id1);
if(id1==1)
{
$("#dis11").show();
adis=document.getElementById("discount").value;
alert(adis);
}
}
</script>
Explanation
Your function prizf run only two times. one starting and second onchange but you are able to get the value of discount when it start to visible for entering values.
you are getting undefined $ error because jquery is not included.
I create a function for getting value of discount.
check the below code.
BTW this is not the standard code.
<script>
function discountajax()
{
p = document.getElementById("prize1").value;
//alert(p);
id1 = document.getElementById("offers").value;
if (id1 == 1)
{
// alert("id"+id1);
$("#dis11").show();
adis = document.getElementById("discount").value;
alert(adis);
}
}
$(document).ready(function () {
$("#discount").focusout(function () {
prizf();
})
})
</script>

How to add same from input area when user press a button?

I am doing a form right now, I want to automatically add another input area, same as the line above, when user press a button.
<div class="row" id="1">
<div class="form-group col-lg-2">
<select class="form-control" id="select">
<option selected>Tag Name</option>
<option value="p">p</option>
<option value="br">br</option>
</select>
</div>
<div class="form-group col-lg-2">
<select class="form-control" id="class">
<option selected>Tag Class</option>
<option value="Day">Day</option>
<option value="BlockTime">BlockTime</option>
<option value="BlockTitle">BlockTitle</option>
<option value="Session">Session</option>
<option value="Person">Person</option>
<option value="Firm">Firm</option>
</select>
</div>
<div class="form-group col-lg-7">
<textarea class="form-control" rows="3" id="textArea">Please put the content inside this html tag.</textarea>
</div>
<div class="form-group col-lg-1">
<input type="button" class="btn btn-default" onclick="addLine()" value="Add">
</div>
</div>
This is a line of input area, I want to add the same html below the input area we have now when user press the "Add" button. Maybe using JQuery?
It should looks like this one.
This is what I tried:
function addLine() {
$('#1').clone().attr('id', '').appendTo('form');
}
For now, it seems work, but how should I do if I want to add id to the new created element, say, 2, 3, 4?
Also I am not sure am I did it the right, or best way.
Solution by jQuery
var n = 8 // adjust it in some way
var inputArea = ['<div class="form-group col-lg-'+n+'">',
'<textarea class="form-control" rows="3" id="textArea-'+n+'">',// let's be nice and not use same IDs twice
'Please put the content inside this html tag.',
'</textarea></div>'].join('')
$('.row').append(inputArea);
However make sure that your back end is ready to handle that input.
EDIT:
The solution might not be fancy and using clone() is completely fine too.
To keep track of ids I would add a simple variable like n that I would increment every time a new input area is added, and then add it to id.
So, init
var n = 0;
In addLine:
n++;
Set the id (doable in addLine too)
$target.attr('id', 'inputArea-'+n);//Assuming that $target is the inputArea in question
You can copy from a blueprint structure in the DOM and append your copy after the button.
var addline = function() {
var invisibleNewLine = jQuery('#blueprint').clone();
var visibleNewLine = invisibleNewLine.removeClass('invisible');
jQuery('#target').append(visibleNewLine);
};
jQuery('#add-line').click(addline);
Remove the onClick handler on the element and bind the event using jQuery.
<button id="add-line" class="btn btn-default">Add</button>
See the fiddle here: JSFiddle

Sorting Input Text inside multiple div's using javascript / jquery?

There are three fields(first name, Last name & age) displayed in text boxes. Each field is displayed in separate div's. There are 4 records. On clicking a sort button above each field the div records should be sorted based on the data type of the field and should displayed in the HTML page.
I tried the solution in this link
I can't use this because the records are displayed in text box within the div.
<div id="content">
<div>
<div class="price"><input type="text" class="pri" value="120"/></div>
<div class="dateDiv">2012-05-09 20:39:38.0</div>
<div class="distance">20 mile</div>
</div>
<div>
<div class="price"><input type="text" class="pri" value="123"/></div>
<div class="dateDiv">2012-05-10 20:39:38.0</div>
<div class="distance">30 mile</div>
</div>
<div>
<div class="price"><input type="text" class="pri" value="100" /></div>
<div class="dateDiv">2012-05-11 20:39:38.0</div>
<div class="distance">50 mile</div>
</div>
<div>
<div class="price"><input type="text" class="pri" value="124"/></div>
<div class="dateDiv">2012-05-12 20:39:38.0</div>
<div class="distance">60 mile</div>
</div>
</div>
How can I do this in javascript?
see this demo using jquery.tinysort.min.js
see also this one
You can use knockoutjs to do something like that.
For example, the html:
<div id="content" data-bind="foreach: lines">
<div class="fname"><input type="text class="pri" data-bind="value: fname"/></div>
<div class="lname" data-bind="text: lname"/>
<div class="age" data-bind="text: age"/>
</div>
Javascript:
function EachDivViewModel(line)
{
this.fname = line.fname;
this.lname = line.lname;
this.age = line.age;
}
function YourViewModel()
{
var self = this;
self.lines = ko.observableArray([]); // this array will contain elements of EachDivViewModel type
self.handlerForYourSortButtongs = function() {
// Code here to sort the array based on the button clicked
// The UI will automatically get updated as you reorder the elements in the lines array
}
}
$(document).ready(function() {
var yourViewModelInstance = new YourViewModel();
// Code to get the lines here
ko.applyBindings(yourViewModelInstance);
});
It can be done using tablesorter.
Hope that will help you out.

Categories

Resources