You're really close. The issue with getting undefined
for contentElement
and titleElement
is described in this question's answers, but the answers there, being general, don't give you the simplest way to deal with it in your case.
See comments:
function getElements() {
loadJSON(function(response) {
var elements = JSON.parse(response);
var containerElement = document.getElementById("element");
var entryElement;
var titleElement;
var contentElement;
var deleteElement;
for (var i = 0; i < elements.length; i++) {
// Create a container for the entry; let's give it a class we'll
// use later as well
entryElement = document.createElement("div");
entryElement.className = "entry";
titleElement = document.createElement("h1");
contentElement = document.createElement("p");
deleteElement = document.createElement("button");
// Put these in the entry's container
entryElement.appendChild(titleElement);
entryElement.appendChild(contentElement);
entryElement.appendChild(deleteElement);
titleElement.innerHTML = elements[i].title;
contentElement.innerHTML = elements[i].content;
deleteElement.innerHTML = "Delete";
deleteElement.addEventListener('click', function() {
// Here, `this` refers to the delete button that was clicked;
// remove its parent element:
var parent = this.parentNode;
parent.parentNode.removeChild(parent);
});
// Add the entry to the main container
containerElement.appendChild(entryElement);
}
});
}
Separately, there's no need to re-load the JSON in addElement
, just add a new element live to the page. In fact, since we want to do that both when creating the elements from the JSON and when adding a new one, let's put that in a function, then reuse that function:
'use strict'
var json = '[{' +
'"title": "Hello World !",' +
'"content": "1111111"' +
'},' +
'{' +
'"title": "Lorem Ipsum",' +
'"content": "22222222"' +
'},' +
'{' +
'"title": "azertyuiop",' +
'"content": "33333333"' +
'}' +
']';
function loadJSON(callback) {
// Simulate the ajax
setTimeout(callback, 200, json);
/*
var xobj = new XMLHttpRequest();
xobj.overrideMimeType("application/json");
xobj.open('GET', 'json/elements.json', true);
xobj.onreadystatechange = function() {
if (xobj.readyState == 4 && xobj.status == "200") {
callback(xobj.responseText);
}
};
xobj.send(null);
*/
}
function addElement(title, content) {
// Create a container for the entry
var entryElement = document.createElement("div");
entryElement.className = "entry";
// And the things to put in it
var titleElement = document.createElement("h1");
var contentElement = document.createElement("p");
var deleteElement = document.createElement("button");
// Put these in the entry's container
entryElement.appendChild(titleElement);
entryElement.appendChild(contentElement);
entryElement.appendChild(deleteElement);
// You might consider HTML-escaping things, here's how
titleElement.appendChild(document.createTextNode(title));
contentElement.appendChild(document.createTextNode(content));
deleteElement.innerHTML = "Delete";
deleteElement.addEventListener('click', function() {
// Here, `this` refers to the delete button that was clicked;
// remove its parent element:
var parent = this.parentNode;
parent.parentNode.removeChild(parent);
});
// Add the entry to the main container
document.getElementById("element").appendChild(entryElement);
}
function getElements() {
loadJSON(function(response) {
var elements = JSON.parse(response);
for (var i = 0; i < elements.length; i++) {
// Call our reusable function
addElement(elements[i].title, elements[i].content);
}
});
}
function addElementClick() {
// Just call our reusable function
addElement(document.getElementById('newTitle').value, document.getElementById('newContent').value);
}
getElements();
<div id="element"></div>
<div id="newElement">
<input id="newTitle" type="text" value="">
<input id="newContent" type="text" value="">
<button type="button" name="addElement" onclick="addElementClick()">Add</button>
</div>
Note that there I renamed your original addElement
to addElementClick
and had a new addElement
function accept the title and content as parameters.
If at some stage you want to rebuild the elements
array, you can readily do that by retrieving the data from the DOM:
function getElementsFromUI() {
var elements = [];
document.querySelector(".entry").forEach(function(entryElement) {
elements.push({
title: entryElement.querySelector("h1").innerHTML,
content: entryElement.querySelector("p").innerHTML
});
});
return elements;
}
The NodeList
returned by querySelectorAll
only recently got forEach
, but you can easily polyfill it on older browsers:
if (typeof NodeList !== "undefined" && NodeList.prototype && !NodeList.prototype.forEach) {
Object.defineProperty(NodeList.prototype, "forEach", {
value: Array.prototype.forEach,
configurable: true,
enumerable: true
});
}
Live Example:
'use strict'
var json = '[{' +
'"title": "Hello World !",' +
'"content": "1111111"' +
'},' +
'{' +
'"title": "Lorem Ipsum",' +
'"content": "22222222"' +
'},' +
'{' +
'"title": "azertyuiop",' +
'"content": "33333333"' +
'}' +
']';
if (typeof NodeList !== "undefined" && NodeList.prototype && !NodeList.prototype.forEach) {
Object.defineProperty(NodeList.prototype, "forEach", {
value: Array.prototype.forEach,
configurable: true,
enumerable: true
});
}
function loadJSON(callback) {
// Simulate the ajax
setTimeout(callback, 200, json);
/*
var xobj = new XMLHttpRequest();
xobj.overrideMimeType("application/json");
xobj.open('GET', 'json/elements.json', true);
xobj.onreadystatechange = function() {
if (xobj.readyState == 4 && xobj.status == "200") {
callback(xobj.responseText);
}
};
xobj.send(null);
*/
}
function addElement(title, content) {
// Create a container for the entry
var entryElement = document.createElement("div");
entryElement.className = "entry";
// And the things to put in it
var titleElement = document.createElement("h1");
var contentElement = document.createElement("p");
var deleteElement = document.createElement("button");
// Put these in the entry's container
entryElement.appendChild(titleElement);
entryElement.appendChild(contentElement);
entryElement.appendChild(deleteElement);
// You might consider HTML-escaping things, here's how
titleElement.appendChild(document.createTextNode(title));
contentElement.appendChild(document.createTextNode(content));
deleteElement.innerHTML = "Delete";
deleteElement.addEventListener('click', function() {
// Here, `this` refers to the delete button that was clicked;
// remove its parent element:
var parent = this.parentNode;
parent.parentNode.removeChild(parent);
});
// Add the entry to the main container
document.getElementById("element").appendChild(entryElement);
}
function getElements() {
loadJSON(function(response) {
var elements = JSON.parse(response);
for (var i = 0; i < elements.length; i++) {
// Call our reusable function
addElement(elements[i].title, elements[i].content);
}
});
}
function addElementClick() {
// Just call our reusable function
addElement(document.getElementById('newTitle').value, document.getElementById('newContent').value);
}
function getElementsFromUI() {
var elements = [];
document.querySelectorAll(".entry").forEach(function(entryElement) {
elements.push({
title: entryElement.querySelector("h1").innerHTML,
content: entryElement.querySelector("p").innerHTML
});
});
return elements;
}
document.getElementById("btnShow").addEventListener("click", function() {
console.log(getElementsFromUI());
});
getElements();
.as-console-wrapper {
max-height: 100% !important;
}
<input type="button" id="btnShow" value="Show Elements">
<div id="element"></div>
<div id="newElement">
<input id="newTitle" type="text" value="">
<input id="newContent" type="text" value="">
<button type="button" name="addElement" onclick="addElementClick()">Add</button>
</div>
Alternately, you could keep a copy of the elements
array around and ensure that you both remove the DOM structure for an entry and remove the entry, and similar ensure adding is done in parallel, but then you're writing code to maintain two lists of things, which almost inevitably leads to updating one but forgetting to update the other. Various "live bindings" libraries try to help there by having you just maintain your elements array and then have the DOM updated based on it, such as Knockout, Vue, React, Angular, and a dozen others. (Some are quite simple and drop easily in to any project; others are much more complex and take the "you do your app OUR way" approach. :-) ) For anything non-trivial, it's probably worth looking at something like that.