Javascript - Navigation as a loop - javascript

I would like to create a simple navigation:
<ul>
<li>Div1-name</li>
<li>Div2-name</li>
<li>Div3-name</li>
</ul>
When clicked, it goes to the div with that id. I don't want to do this permanently, it's supposed to change in the loop because the user can add new items and wants them to be looped.
The user adds new div - with a unique name and ID. How to construct a loop?
It's best to set a constant class for divs (e.g. gohere), you have to load it via javascript, then do li elements in a loop.
Can anyone help?
And these are the elements that the user adds:
<div class="divdiv" id="div_id">
<h3>DIV TITLE</h3>
<br>
Description
<br>
<p>Description</p>
<hr>
</div>

OK, it's really difficult to understand exactly what you ask for, but here (a bit hacky) an example. I hope that it can help you in the right direction.
var items = document.getElementById('items');
var main = document.getElementById('main');
items.addEventListener('click', e => {
e.preventDefault();
if (e.target.nodeName == 'A') {
console.log(`You clicked ${e.target.attributes['href'].value}`);
}
});
document.forms.new.addEventListener('submit', e => {
e.preventDefault();
let newid = items.children.length + 1;
console.log(newid);
let li = `<li>Div${newid}-name</li>`;
items.innerHTML += li;
let div = `<div class="divdiv" id="div${newid}">
<h3>DIV TITLE</h3><br>Description<br><p>Description</p><hr></div>`;
main.innerHTML += div;
});
<ul id="items">
<li>Div1-name</li>
<li>Div2-name</li>
<li>Div3-name</li>
</ul>
<form name="new">
<button>Add new</button>
</form>
<div id="main"></div>

Keep the navigation details in an array, and then iterate over it.
Use DOMParser to parse the HTML the user adds to extract the navigation information.
const arr = [
{ href: "div1", name: 'Div1-name' },
{ href: "div2", name: 'Div2-name' },
{ href: "div3", name: 'Div3-name' }
];
const nav = document.querySelector('#nav');
const main = document.querySelector('#main');
const text = document.querySelector('textarea');
const button = document.querySelector('button');
button.addEventListener('click', handleInput, false);
const parser = new DOMParser();
function handleInput() {
// Add the HTML to the page
main.insertAdjacentHTML('beforeend', text.value);
// Parse the HTML
const frag = parser.parseFromString(text.value, 'text/html');
// Extract the id (href) and name
const href = frag.querySelector('div').id;
const name = frag.querySelector('h3').textContent;
// Add this to the navigation array
// to the array
arr.push({ href: `#${href}`, name: name });
updateView();
}
function updateView() {
// `map` is useful here. It will produce a new array
// (of HTML), but make sure you `join` it up at the end
const links = arr.map(({ href, name }) => {
return `<li>${name}</li>`;
}).join('');
nav.innerHTML = `<ul>${links}</ul>`;
}
updateView();
<div id="nav"></div>
Add HTML:
<textarea></textarea>
<button>Add</button>
<div id="main"></div>

Related

How to return innertext of sibling element

In GTM i'm trying to return the inner text of a sibling element of the clicked element.
<div class="repair-item-n ">
<div class="repair-slide--54894d33-6c88-488f-95d7-3ec9b6a3ade4">
<div class="restoration_wrap text-center">
<img class="restoration-image">
</div>
<p class="title">Bags</p>
</div>
</div>
For example, on click of class "restoration-image" I want to return the value "Bags".
I have multiple occurrences of this HTML on the page with varinats such as "Shoes", "Hats" etc so I want to know on click of each, which would be the respective text of the "title" class
Try this:
var images = document.querySelectorAll('.restoration-image');
images.forEach(function (image) {
image.addEventListener('click', function (event) {
var parent = this.parentNode;
var nextSibling = parent.nextElementSibling;
alert(nextSibling.innerText)
})
});
This should work for GTM custom JavaScript variables.
function getImgTitle() {
if({{event}} === 'gtm.click') {
var image = {{Click Element}};
var parent = image.parentNode,
nextSibling = parent.nextElementSibling;
return nextSibling.innerText;
}
}
I would do this in a Custom Javascript Variable:
Click - Repair Item Title
function() {
var clickedEl = {{Click Element}};
if (!clickedEl) return;
// Find the mutual parent element
var repairItemEl = clickedEl.closest(".repair-item-n");
if (!repairItemEl) return;
var titleEl = repairItemEl.querySelector(".title");
if (!titleEl) return;
return titleEl.innerText
}}

Can't remove an Item from LocalStorage

Hello guys I creating an ecommerce part of the website,could you please help me with removing element from local storage,I don't want to create multiple functions for removing each element,but still nothing works,help me please
let pList = document.getElementById("productList")
//Espresso
const addEspresso = () => {
var Espresso, esp, eObj;
Espresso = {
name: "Espresso",
type: "strong",
imgSrc: "images/c7.png"
};
localStorage.setItem("Espresso", JSON.stringify(Espresso));
esp = localStorage.getItem("Espresso");
eObj = JSON.parse(esp);
let htmlEspresso = "";
htmlEspresso += `
<div class="productDiv">
<p>${eObj.name}</p>
<p>${eObj.type}</p>
<img src="${eObj.imgSrc}">
<button class="btn-remove">REMOVE</button>
</div>
`
document.getElementById("productList").innerHTML += htmlEspresso
}
document.addEventListener("click", function(e) {
var btnR = document.getElementsByClassName("btn-remove");
if (e.target.classList.contains("btn-remove")) {
e.target.closest(".productDiv").remove();
//localStorage.removeItem(e.target.parentElement) - This does not work,I do not understand what to do with it
}
})
.parentElement will return you the DOM element not the text. You can do some parsing after it or check for you particular p element inside it.
Your first p child will hold the text Espresso. You can get it using .textContent. This is the key you saved in the storage.
localStorage.removeItem(e.target.parentElement.querySelector('p').textContent);

How Dynamically Created Button Target Individual Element

I am under the impression if we target an element by element name of class name it will be applicable to all elements or elements with that class name. But in the following code when I clicked a button it will only change the innerHTML of the nearest span element even though I have add event listener by selecting just button element. Please clarify. Thanks.
const myArray = [0, 1, 2];
let myContainer = document.querySelector('.container');
const newArray = myArray.map((item) => {
let newArticle = document.createElement('article');
const myHTML = `<article>
<span></span>
<button>Click ${item}</button>
</article>
`;
newArticle.innerHTML = myHTML;
let myBtn = newArticle.querySelector('button');
myBtn.addEventListener('click', (e) => {
newArticle.querySelector('span').innerHTML = 'clicked';
})
return newArticle;
});
newArray.forEach((item) => {
myContainer.appendChild(item);
});
<div class="container">
<section>
</section>
</div>
let myBtn = newArticle.querySelector('button');
returns the FIRST button in newArticle
let myBtn = document.querySelectorAll('button');
returns ALL buttons in document
You can tell us what you expect by clicking on each one button?

How to append HTML to a div whilst still allowing for listeners to work?

I have the following HTML I want to append to a div:
var map_div =
"<div class=\"maps\">"
+ "<h2>View marker <button class=\"close\" data-what=\"maps\">[ close ]</button></h2>"
+ "<div class=\"the-contents\" id=\"mapDiv" + link_id + "\"></div>"
+ "</div>";
I can do this with:
document.getElementById("listing"+link_id).insertAdjacentHTML('beforeend', map_div );
This adds the HTML into the div properly - but there is a problem problem with this, in that it doesn't seem to keep the listeners on button.close. This works fine if I put that HTML in directly in the original code (and don't try to to inject it into the DOM). Is there something I'm missing? Do I need to create each individual element, then add it via appendChild, and then add another element into that new element? Seems a bit long winded :/
Thanks
You can add listener to the container and use custom properties to identify the element's role and id:
document.querySelector("button").addEventListener(
"click",
e=>addItem(++startID)
)
var startID=0;
const container = document.getElementById("container");
const addItem = itemID => {
const el = document.createElement("div");
el.innerHTML = `
say hi
remove
${itemID}
`;
el.setAttribute("data-link-container-id",itemID);
container.appendChild(el);
}
const handleClickable = e => {
e.preventDefault();
const id= e.target.getAttribute("data-id");
console.log("hello:",id);
}
const handleRemove = e => {
e.preventDefault();
const id= e.target.getAttribute("data-id");
container.querySelector(`[data-link-container-id="${id}"]`).remove();
}
//add event listener to container and use data-role to see what's clicked
container.addEventListener(
"click",
e=>{
const role = e.target.getAttribute("data-role");
if(role==="say-hi"){
return handleClickable(e);
}
if(role==="remove"){
return handleRemove(e);
}
}
)
<div id="container">
<button>add</button>
</div>

Struggling to keep HTML and JS separate

I'm creating a basic to do list in Vanilla JS, I'm using Handlebars to keep the HTML & JS separate.
Everything was going fine till I came to the delete method. Because my delete button is inside my HTML and not created inside my JS I'm finding it hard to select and delete items from the array.
I thought I'd found a way around it by looping over them but the issue with this is it tries to grab the buttons on page load, and so it returns always an empty array as on page load there are no delete buttons as no to do has been added at that point.
I've also tried putting the delete method inside the add method to counter this but this also presented issues.
Simply, can someone give me an example of a working delete method that removes the relevant item from the array using splice.
Cheers
HTML
<input id="add-to-do-value" type="text" placeholder="Add to do">
<button id="add-to-do">Add</button>
<div id="to-do-app"></div>
<script type="text/javascript" src="js/handlebars.js"></script>
<script id="to-do-template" type="text/template">
<ul>
{{#this}}
<div>
<li id={{id}}>
{{value}}
<button class="delete-btn" id={{id}}>Delete</button>
</li>
</div>
{{/this}}
</ul>
</script>
<script type="text/javascript" src="js/app.js"></script>
JS
(function() {
// Data array to store to dos
var data = [];
// Cache dom
var toDoApp = document.getElementById('to-do-app');
var toDoTemplate = document.getElementById('to-do-template');
var addToDo = document.getElementById('add-to-do');
var addToDoValue = document.getElementById('add-to-do-value');
var toDoTemplate = Handlebars.compile(toDoTemplate.innerHTML);
// Render HTML
var render = function() {
toDoApp.innerHTML = toDoTemplate(data);
}
// Add to dos
var add = function() {
var toDoValue = addToDoValue.value;
if(toDoValue) {
var toDoObj = {
value: toDoValue,
id: Date.now(),
}
data.push(toDoObj);
}
render();
}
// Delete to dos
var deleteBtn = document.querySelectorAll('.delete-btn');
for(i=0; i<deleteBtn.length; i++) {
deleteBtn[i].addEventListener("click", function(){
for(j=0; j<data.length; j++) {
if(data[j].id == this.id) {
data.splice(data[j], 1);
render();
}
}
});
}
// Bind events
addToDo.addEventListener("click", add);
})();
The fact that you're using Handlebars makes the whole thing unnecessary complex. I would suggest that you don't use innerHTML, but other parts of the DOM API instead to be able to easily access the elements you need. For more complex todo items, I would consider using <template>s.
Anyway, you have to bind the event listener for removing the item when you create the new item (i.e. in the add function):
var todos = [];
var input = document.querySelector('input');
var addButton = document.querySelector('button');
var container = document.querySelector('ul');
var add = function () {
var content = input.value;
input.value = '';
var id = Date.now();
var li = document.createElement('li');
li.appendChild(document.createTextNode(content));
var button = document.createElement('button');
button.textContent = 'Delete';
button.addEventListener('click', remove.bind(null, id));
li.appendChild(button);
todos.push({ content, id, element: li });
container.appendChild(li);
};
var remove = function (id) {
var todo = todos.find(todo => todo.id === id);
container.removeChild(todo.element);
todos = todos.filter(t => t !== todo);
};
addButton.addEventListener('click', add);
<input type="text" placeholder="Add to do">
<button>Add</button>
<ul></ul>

Categories