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);
Related
I've posted a similar question regarding switching HTML text snippets (see JS Switch HTML content based on specific text included in h1 tag), for which the replies were hugely helpful. From these replies, I used the following code to change the HTML text snippet:
<h1>This is about Red Apples</h1>
<span id="colour"></span>
window.addEventListener("load",function(){
const h1 = document.querySelector("h1"),
description = document.querySelector("#colour"),
colours = [
{ colour: "red", description: "red description" },
{ colour: "blue", description: "blue description" }
]
description.innerText = colours.filter(f => h1.innerText.toLowerCase().includes(f.colour))[0]?.description
},false);
I now wish to modify the code to change the img src depending on some text (a different string of text from that required in my previous post) included in the h1 tag.
After lots of trial and error I can't figure out how to modify this code to change the image src on the same page. The closest I've got so far, but using the "if/else" method, is:
<h1>This is about Red Apples</h1>
<img id="fruitimage" src="default-image.jpg"/>
var map = {
'red apples': 'redapples-image.jpg',
'blue blueberries': 'blueberries-image.jpg',
'yellow bananas': 'yellowbananas-image.jpg'
};
function influenceImage(source, map) {
if (!source || !map) {
return false;
}
else {
var text = source.textContent || source.innerText;
for (var word in map) {
if (map.hasOwnProperty(word) && text.indexOf(word) !== -1) {
return map[word];
}
}
}
}
document.getElementById('fruitimage').imageNode.src = influenceImage(document.getElementsByTagName('h1')[0], map);
However, I still cannot get this to work. I'm really hoping someone can help! Many thanks in advance!
For what I see you have 2 errors in your code.
1 - remove .imageNode.
2 - you need to convert text to lower-case as the object key you are trying to get is in lower-case, use .toLowerCase()
var map = {
"red apples": "redapples-image.jpg",
"blue blueberries": "blueberries-image.jpg",
"yellow bananas": "yellowbananas-image.jpg",
};
function influenceImage(source, map) {
if (!source || !map) {
return false;
} else {
let text = source.textContent.toLowerCase() || source.innerText.toLowerCase();3
for (var word in map) {
if (map.hasOwnProperty(word) && text.indexOf(word) !== -1) {
return map[word];
}
}
}
}
document.getElementById("fruitimage").src = influenceImage( document.getElementsByTagName("h1")[0], map);
This should work for what you are trying to achieve
var map = {
'red apples': 'redapples-image.jpg',
'blue blueberries': 'blueberries-image.jpg',
'yellow bananas': 'yellowbananas-image.jpg'
};
function retImage(source) {
var imageSource = "default-image.jpg";
var text = document.getElementsByTagName('h1')[0].innerText;
for (const [key, val] of Object.entries(map)) {
if (text.toLowerCase().includes(key)) {
imageSource = val;
}
}
let el = document.createElement("img");
el.setAttribute("src", imageSource);
el.setAttribute("alt", imageSource);
document.getElementById("fruitimage").appendChild(el);
};
retImage() // function to return image
<h1>This is about Red Apples</h1>
<div id="fruitimage"></div>
👋
I think your example misses a few bits here and there.
You still need some sort of an event listener to check if the DOM is fully loaded before you try and get any handle of your elements with document.getElementById in your case. (or place the JS script UNDER the dom, literally)
influenceImage name is pretty vague and is not doing what the name implies.
// Utility function.
function getImageSrcByDescription(description) {
const normalizedDescription = description.toLowerCase().trim()
// Iterate over the array one item at a time.
const image = IMAGE_TAGS_PAIRS.find((obj) => {
// We split the tags by empty space delimiter.
const tags = obj.tags.split(' ');
// ... and make sure the provided description will contain all of the tags.
const isMatchingAllTags = tags.every(tag => normalizedDescription.includes(tag))
return isMatchingAllTags ? obj : undefined
})
// If image is found, return the `src` property of the object, if not, fallback.
return image ? image.src : TAG_NOT_FOUND_IMG
}
// Act on page load.
window.addEventListener("load", function() {
// Get handles for both description & image elements.
const fruitDescriptionElem = document.querySelector('#fruit-description');
const fruitImageElem = document.querySelector("#fruit-image");
const description = fruitDescriptionElem.innerText
const imageSrc = getImageSrcByDescription(description)
if (imageSrc) {
fruitImageElem.alt = 'New Image'
fruitImageElem.src = imageSrc
}
})
Here's a fiddle for you to play around.
https://jsfiddle.net/xjL79zef/1/
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>
I'd like to use pure JS to check if some String, the textareas .innerHTML = newContent below, contains some tag (h1in my case) at the beginning (=as first child). What would be the best way to do this?
Thanks!
function submitNewSectionContent(e) {
for (var i = 0; i < sections.length; i++)
let newHeading = document.getElementById('edit-title').value;
/* edit-title is text-input*/
let newContent = document.getElementById('edit-sectionText').innerHTML;
/* edit-sectionText is textarea */
if (newContent.indexOf('<h1>') > -1 && newContent.indexOf('<h1>') < 10) { /* <h1> is at beginning so replace with newHeading */
let toberemoved = newContent.match('<h1>.*<\/h1>');
newContent = newContent.replace(toberemoved[0], '').trim();
sections[i].innerHTML = '<h1>'+newHeading+'</h1>' + sections[i].innerHTML;
} else { /* newContent has no h1 as first child, so add h1 from newHeading */
sections[i].innerHTML = '<h1>'+newHeading+'</h1>' + newContent;
}
}
}
Problem with Regular expressions is they do not really work well with HTML. So Your best bet is to convert it to a DOM fragment and do the manipulations and convert it back. Only issue with this method really is you can lose formatting. There are libraries out there that can pretty print HTML.
function updateHeadline(txt) {
const ta = document.querySelector("textarea");
const data = ta.value; // read value, not innerHTML
const temp = document.createElement('div'); // temp div to hold html
temp.innerHTML = data; // set the html to the temp element
let firstChild = temp.firstElementChild // look at the dom
if (!firstChild || firstChild.tagName!=="H1") { // see if we have an h1
firstChild = document.createElement("h1") // if not create one
temp.prepend(firstChild) // add it to the front
}
firstChild.innerHTML = txt // set the new text of the h1
ta.value = temp.innerHTML // put the content back into the textarea
}
const btn = document.querySelector("button");
btn.addEventListener("click", function (e) {
e.preventDefault()
updateHeadline(document.querySelector("#text").value)
})
textarea {
width: 300px;
height: 200px;
}
<textarea>
<p>Some other text</p>
<p>Some more text</p>
</textarea>
<input value="foo" id="text"/>
<button>Set</button>
You could use regex like so, (updated based on comment)
if( /^\s*<h1>/gi.test(stringToTest) ) {
//logic here
}
It checks if the stringToTest begins with ^ tag
See here : https://regex101.com/r/vSo4sL/1
convert to dom, and parse dom
this portion of code makes it possible to treat a chain to retrieve titles placed in the H1 tag and (on the fly) treat the string of characters.
It's easily expandable for future processing : or tag processing or other ...!
commented code
<html>
<script>
var s="<H1>Hey Title</H1>\n Hello,\n other title <H1>Green!</H1>\n Ipsum dolore sit...";
console.log(s);
console.log("-------------------------");
var partialDoc = document.createElement( 'html' );
partialDoc.innerHTML = s;
var parsed='';
var titles=[];
treatment(partialDoc);
console.log("\n-------------------------\n");
console.log("parsed",parsed);
console.log("\n-------------------------\n");
console.log("s var contains "+titles.length+ " H1 tag");
console.log("titles "+titles);
function treatment(root) {
var child = root.firstChild;
while (child) {
// child.nodeName = H1 | H2 | P etc...
// child.nodeType = 1
// catch H1
if (child.nodeName=='H1') {
// append your title,
parsed+=" [H1 FOUND content= {"+child.innerText+"} H1])";
// or
// parsed+="<H1>"+child.innerText+"<H1>";
// add your own process here
// add this title in array
// or what you want...
titles.push(child.innerText);
// next part of document
child = child.nextSibling;
continue;
}
// capture other text than H1
if (child.nodeType==3) { // Node Type Text
parsed+=child.nodeValue;
}
if (child.nodeType==1) { // Node Type ELEMENT, : sub nodes...
treatment(child);
}
// continue the rest of doc
child = child.nextSibling;
}
}
</script>
</html>
One way you could do it is: Node.firstElementChild which will avoid giving child node as #text for white-spaces and Node.nodeName
let firstChild = document.getElementById('edit-sectionText').firstElementChild;
if(firstChild.nodeName === "H1"){
firstChild.innerHTML = "Replacement Value"
}
Note & Update: The earlier api that I had suggested Node.firstChild will not prevent white-spaces which gives #text node and comments as #comment node.
2nd Way: Node.children and picking the first child out of it should have a similar result to Node.firstElementChild.
let elem = document.getElementById('edit-sectionText');
if(elem){
let firstChild = elem.children[0];
}
Update based on comments: Using Dom Parser Interface
The interface allows to parse XML or HTML source from a string based on the mime type provided for its method parseFromString(string, mimeType)
It will give the top level #document node with parsed HTML from the string where if exists <h1> or <H1> at the beginning would be the first child of body and subsequently can be tested via tagName property.
Note: Takes care of preceding HTML comments and spaces at the beginning but a caveat is doesn't check fully closed tags ex: var s = \t <h1>I am a heading <h1> here the <h1> was never closed and in the result will two fully formed headings at the body with content : I am a heading and ""
let textAreaString = document.getElementById("edit-sectionText").value;
const domParser = new DOMParser();
const parsedDoc = domParser.parseFromString(textAreaString, "text/html");
if (parsedDoc.body.firstElementChild.tagName === "H1") {
//yes it starts with <h1> or <H1>
}
I'm writing a javascript program that gets the input in any of the below forms.
"Here is the result \"google\", and \"yahoo\""
or
"Here is a plain result"
and this is stored in a variable say X. and I want to create an anchor tag when ever I come across an anchor tag. I know that a href will by default create an anchor tag but in my case the result is rendered as a text, here is my code that I've tried so far.
var newLink = document.createElement('a');
newLink.href = 'http://google.com';
newLink.innerHTML = 'My anchor';
if (message) {
var x = message;
console.log(x.includes("href"));
if (!x.includes("href")) {
responsePara.appendChild(document.createTextNode(message));
responsePara.appendChild(document.createElement('br'));
} else {
//responsePara.appendChild(document.createTextNode(message));
responsePara.appendChild(newLink);
responsePara.appendChild(document.createElement('br'));
}
}
the output that I'm expecting is in case 1
<p> Here is the result "google", and "yahoo"</p>
in case 2
<p>Here is a plain result</p>
please let me know on how can I do this.
Note I'm using only js, no jquery
I can't see your problem, it should be really easy to implement this, right?
All you need to parse is the input that is coming to HTML. within another element (in your case p element)
UPDATE
I have updated this question, so you can modify (or create if there is not ref) an existing element with not parsed a element or with plain text.
function createOrUpdateCompositeLink(input, ref) {
if (ref) {
var isLinkText = input.match(/href/g);
var elementChild;
if (isLinkText) {
elementChild = document.createElement('span');
elementChild.innerHTML = input;
} else {
elementChild = document.createTextNode(input);
}
ref.appendChild(elementChild);
return ref;
} else {
var element = document.createElement('p');
element.innerHTML = input;
return element;
}
}
/* USAGE */
var message;
var element;
message = "Here is the result ";
message1 = "google\"";
message2 = " something plain text ";
message3 = ", and \"yahoo\"";
var reference = document.querySelector('.ref');
var el;
createOrUpdateCompositeLink(message, reference);
createOrUpdateCompositeLink(message1, reference);
createOrUpdateCompositeLink(message2, reference);
createOrUpdateCompositeLink(message3, reference);
<div class="ref"></div>
I would suggest you consider using jQuery and what you are trying to do becomes:
jQuery(".response").append(message);
I assume that your responsePara variable is defined from an existing <div> somewhere. In my example, that <div> would have a class named response.
<div class="response"></div>
Once you get a message, it gets added to the response div but that one line jQuery() command.
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>