Quick question... what is the reason that I need to return the 'element' variable when using the appendToLi? If I remove the line, the code will not add anything to the list. It's confusing to me because the appendToLi function calls are not returning the element to anything.
For example, I would understand if it looked like this...
let element = appendToLi(property, value, text);
But that is not the case. I have...
appendToLi(property, value, text);
document.addEventListener('DOMContentLoaded', () => {
//Targets the unordered list element
const list = document.getElementById("myUL");
//Targets the children of the unordered list
const li = list.children;
//Targets the form element.
const form = document.getElementById("registrar");
//Function declaration (Begins process of creating list item)
function createListItem(text){
//Creates the list item
const li = document.createElement('li');
//Function delcaration (creates an element and returns element)
function createElement(elementName, property, value) {
const element = document.createElement(elementName);
element[property] = value;
return element;
}
//Function declaration (Adds the created element to the list)
function appendToLi(elementName, property, value){
const element = createElement(elementName, property, value);
li.appendChild(element);
return element;
}
//Appends all children to the list item.
appendToLi('span', 'textContent', text);
appendToLi('label', 'textContent', 'Completed')
.appendChild(createElement('input', 'type', 'checkbox'));
appendToLi('button', 'textContent', 'remove');
appendToLi('button', 'textContent', 'edit');
/*Returns the list item and it's children to what has called the
createListItem function*/
return li;
}
//Event listener (listens for click on submit button/enter press)
form.addEventListener('submit', (e) => {
e.preventDefault();
//Targets the input element.
const input = document.querySelector('input');
//If the user has not entered any text in the input field, alerts.
if(input.value === '') {
alert('Please enter an item!');
//Otherise begins the process of creating the list item.
} else {
//Holds the user text input.
const text = input.value;
/*Calls the createListItem function which will begin the process
through various other functions.*/
const listItem = createListItem(text);
list.appendChild(listItem);
input.value = '';
}
});
This:
appendToLi('label', 'textContent', 'Completed')
.appendChild(createElement('input', 'type', 'checkbox'));
... throws an error and interrupts script execution if appendToLi() doesn't return the element.
The above lines of code are equivalent to this:
var element = appendToLi('label', 'textContent', 'Completed');
element.appendChild(createElement('input', 'type', 'checkbox'));
Here, it's easier to see what happens when appendToLi() doesn't return anything: element becomes undefined and you then try to invoke a method called appendChild on it which throws an error since undefined doesn't have any methods or properties.
I understood that you are creating elements when form is submitted. There is guess what I can make as I don't know why you are doing what. In the code section below:
//Function declaration (Adds the created element to the list)
function appendToLi(elementName, property, value){
const element = createElement(elementName, property, value);
li.appendChild(element);
return element;
}
//Creates the list item
const li = document.createElement('li');
//Appends all children to the list item.
appendToLi('span', 'textContent', text);
Here you created the li after you have declared the function appendToLi function try to declare before the function declaration. That might help.
Related
The parameter of my function is a function. It should create an element but I should still be able to add attributes by using the parameter details.
E.g.:
const addElement = (details) => {
const element = document.createElement('div');
}
addElement(function() {
element.id = 'my-div'; // Not working since element is not defined
});
Well, I have tried to store the element in an object to be able to use it outside of that function.
let element = {};
const displayVideo = (type, details) => {
element = document.createElement(type);
element.width = 200;
element.height = 200;
element.classList.add('my-class'); // <--- THE PROBLEM!
if (details) {
details();
}
document.querySelector('#layer').insertBefore(element, document.querySelector('#el'));
};
displayVideo('VIDEO', function () {
element.controls = true;
});
My element can not be created because of element.classList.add('my-class'); and I don't even get an error message. If I remove that line, it works but I would still like to be able to add a class to that object. How can I do this?
Just pass element into the function. Since you're just editing properties on the object, this won't cause reference vs value errors.
const addElement = (details) => {
const element = document.createElement('div');
if (details) details(element);
return element;
}
const ele = addElement(function(element) {
element.id = 'my-div';
});
console.log(ele);
In this case details could be something like classname.
function element(type, classname) {
var element = document.createElement(type);
if (classname !== undefined) {
element.classList.add(classname);
}
return element;
};
element("div","my-class"); //<div class="my-class"></div>
Of course instead of classname you could use an array or an object and loop through in order to set multiple attributes.
Or you could store the return value of your function in a variable and then add all the attributes:
var myelement = element("div");
myelement.classList.add("my-new-class");
myelement //<div class="my-new-class"></div>
Everything works fine when I append the template onclick. However when the remove events fire it deletes it from the array. It deletes it from the array but when I go to click to append it back to the array, an error fires off. Cannot read property 'content' of null at HTMLButtonElement.. I've double checked my Html and that seems fine. I don't know why it won't re-append or re-render after it was already pushed into the array and then deleted.
// Function to remove item from array onClick
function removeItem(shoppingItems, btns) {
btns.addEventListener('click', (e) => {
let newArr = shoppingItems.filter(item => item.id !== item.id)
shoppingList = newArr
cartMenuItems.innerHTML = newArr
cartItemCount.innerText = newArr.length
return newArr
})
}
// Template Component
btn.addEventListener('click', (e) => {
if (shoppingList.indexOf(cartItem) !== -1) {
return
}
else {
const menuTemplate = document.querySelector('#menu-template').content
const copyMenuTemplate = document.importNode(menuTemplate, true);
copyMenuTemplate.querySelector('.menu-item-title').textContent = cartItem.title;
copyMenuTemplate.querySelector('.menu-price').textContent = cartItem.price;
copyMenuTemplate.querySelector('.menu-img').src = cartItem.image;
const removeBtn = copyMenuTemplate.querySelector('.remove-btn')
cartMenuItems.appendChild(copyMenuTemplate);
shoppingList.push(cartItem)
cartItemCount.innerText = shoppingList.length;
removeItem(shoppingList, removeBtn);
}
I figured it out. I removed the the original menu template from the else statement. I was deleting the model template every time I clicked the button so it couldn't re-render null.
Edit: turns out nothing is actually wrong with the second snippet (my real code). On one page it works, and on another it doesn't. Yea for underlying errors.
I'm creating a DOM element and giving that DOM element to a WeakMap as a key. Then, with JQuery event delegation/event listener, I'm trying to retrieve the saved key but it's returning undefined:
const item = document.createElement("div"), __data = new WeakMap();
__data.set(item, {hello: "123"})
document.body.appendChild(item)
// later on in event delegation
$("div").on("click", function(event) {
const target = event.target, data = __data.get(target);
console.log(data)
// prints undefined
Anyone know what's wrong or an alternative method to save data for a DOM element that doesn't have an ID?
Edit: I'm kinda annoyed that the example I made works but my own code doesn't... (some bits look redundant. This is modeled after my actual code, so not all the missing pieces are here, just pragmatically) but here's the apparently working code:
const __data = new WeakMap();
function buildingItem() {
const item = document.createElement("div");
item.setAttribute("data-action", "delete");
__data.set(item, {hi: 123});
return item;
}
function build() {
const main = document.getElementById("main")
for (let i = 0; i < 3; i++) {
const container = document.createElement("div"), attached = document.createElement("div");
const build = buildingItem(),
data = __data.get(build);
build.classList.add("classified");
data["hello"] = `Item ${i}`
__data.set(build, data);
build.innerText = `Item ${i}`
attached.append(build);
container.append(attached);
main.append(container);
}
}
build()
$(document).on("click", "div.classified[data-action]", function(event) {
const target = event.currentTarget, data = __data.get(target);
console.log(`CTarget Data: ${data["hello"]}`)
})
<div id="main"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
Two possible issues:
target is the innermost element that was clicked. You probably want this or event.currentTarget instead, which is the element on which you hooked the event handler (which may be an ancestor element to target).
Your jQuery code hooks up the click event on all div elements, not just that one, but you only have that one div in the WeakMap. If you click a different div, you'll naturally get undefined because that other div isn't a key in the map.
Here's an example (I've added a span within the div we have in the map to demonstrate #1, and also added a second div to demonstrate #2):
const item = document.createElement("div"), __data = new WeakMap();
__data.set(item, {hello: "123"});
document.body.appendChild(item);
item.insertAdjacentHTML("beforeend", "<span>Click me, I'll work</span>");
document.body.insertAdjacentHTML("beforeend", "<div>Click me, I won't work (I'm not a key in the map)</div>");
$("div").on("click", function(event) {
const target = event.currentTarget, data = __data.get(target);
console.log("with currentTarget:", data);
// Note that using `event.target` wouldn't hav eworked
console.log("with target:", __data.get(event.target));
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
You've mentioned that in your real code you're using event delegation. currentTarget and this are both fine in that case as well:
// Event delegation
$(document.body).on("click", "div.example", function(event) {
const data1 = __data.get(event.currentTarget);
console.log("using currentTarget:", data1);
const data2 = __data.get(this);
console.log("using this:", data2);
});
// Adding the relevant `div`
const item = document.createElement("div"), __data = new WeakMap();
__data.set(item, {hello: "123"});
item.className = "example";
item.textContent = "Some text to make div clickable";
document.body.appendChild(item);
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
let items = document.querySelector('.items'); // class of ul tag
let $li = document.createElement("li");
function add(item) {
$li.textContent = item;
items.append($li);
}
add('soup');
add('shampoo');
If a node is created with createElement in the function, it is reflected normally. However, I am curious about the detailed reason why only the last element of append is added when a li node is created outside of the function like now.
You should create the li node each time inside the function. Otherwise it will have reference to the old node and update only the innerHTML for that node.
let items = document.querySelector('.items'); // class of ul tag
function add(item) {
let $li = document.createElement("li");
$li.textContent = item;
items.append($li);
}
add('soup');
add('shampoo');
This code originally took input.value and added it to the page. I added local storage to this project and the code is already written but I’m having a hard time displaying the input to the page from localStorage. The input is stored in local storage as objects in an array. I wrote a for loop to loop through those values and pass them to functions that builds the element and appends it to the li and later appends to the ul. It’s not displaying on the page and I’m not getting any errors in the console. I'm not sure where to turn so heres my code:
function fetchInvite() {
const rsvpInvite = JSON.parse(localStorage.getItem("Invitees"));
const rsvpList = document.getElementById('invitedList');
for(var i = 0; i < rsvpInvite.length; i++) {
const name = rsvpInvite[i].name;
const confirm = rsvpInvite[i].confirmed;
createLI(name, confirm);
function createLI(name, confirm) {
const li = document.createElement('li');
function createElement(elementName, property, value) {
const element = document.createElement(elementName);
element[property] = value;
return element;
}
function appendToLI (elementName, property, value) {
const element = createElement(elementName, property, value);
li.appendChild(element);
return element;
}
appendToLI('span', 'textContent', name);
appendToLI('label', 'textContent', confirm)
.appendChild(createElement('input', 'type', 'checkbox'));
appendToLI('button', 'textContent', 'edit');
appendToLI('button', 'textContent', 'remove');
return li;
}
}
}
The full project is available here: https://github.com/tianniNakiMyers/RSVP_TeamTreeHouse/blob/master/app.js
The problem with your code is that you probably never called fetchInvite.
Apart from that, here is a refactoring of your code:
function elt(parent, tag, text) { // create element of tagname tag and append it to a parent (if provided) then set its textContent to text and return it
var el = document.createElement(tag);
if(parent) {
parent.appendChild(el);
}
el.textContent = text;
return el;
}
function fetchInvite() {
const rsvpInvite = JSON.parse(localStorage.getItem("Invitees"));
const rsvpList = document.getElementById('invitedList');
for(var i = 0; i < rsvpInvite.length; i++) {
const name = rsvpInvite[i].name;
const confirm = rsvpInvite[i].confirmed;
const li = elt(rsvpList, 'li', ''); // create an li element with empty text and append it to rsvpList
elt(li, 'span', name); // create a span whose text is name and append it to li
elt(elt(li, 'label', confirm), 'input', '').type = 'checkbox'; // create an input append it to a label element that contain the text confirm and that is appended to li, then set type of input to 'checkbox'
elt(li, 'button', 'edit'); // ...
elt(li, 'button', 'remove');
}
}
fetchInvite(); // don't forget to call it