How to render a large amount of html after hitting search button? - javascript

I am building a recipe search app but I am not sure how to render the big chunk of html representing the 30 recipes I want to have displayed. These recipe card obviously have to change based on what kind of meal I search for. How do I Implement the html into my Js so I can make it change based on the meal type?
I tried to do it with insertAdjecentHTML but since it doesn't replaces the old page with the recipe on but rather ads on to it. It didn't worked:
Nevertheless here is the code:
// recipe card markup
const renderRecipe = () => {
const markup = `
<div class="recipe-results__card">
<div class="recipe-results--img-container">
<img
src="${data.data.hits.recipe.image}"
alt="${data.data.hits.recipe.label}"
class="recipe-results__img"
/>
</div>
<p class="recipe-results__categories">
${limitCategories}
</p>
<h2 class="recipe-results__name">
${data.data.hits.recipe.label}
</h2>
</div>
`;
DOM.recipeGrid.insertAdjacentHTML("beforeend", markup);
};
// fn to display the markup for every search res
export const renderResults = recipes => {
recipes.forEach(renderRecipe);
};

concatenate (push to an array) the HTML so you have ONE update
You can use innerHTML instead of insertAdjacent to replace the content
let html = [];
data.data.hits.forEach(recipe => {
html.push(`
<div class="recipe-results__card">
<div class="recipe-results--img-container">
<img
src="${recipe.image}"
alt="${recipe.label}"
class="recipe-results__img"
/>
</div>
<p class="recipe-results__categories">
${limitCategories}
</p>
<h2 class="recipe-results__name">
${recipe.label}
</h2>
</div>
`) });
document.getElementById("recipeGrid").innerHTML = html.join("");

Related

Click to change image is changing the wrong one

So I'm working on a website that has a lot of movies, and people can choose what movies are they favorite, and for that, I have a star image that can be clicked and when clicked that image will change to another one
Like this:
To this:
The problem I have is that the only image that change is the first one, When I click, for example on the star next to the Ratatouille movie, it will change the first star
This is the HTML:
const getMovieHtml = (movie) => `<div class="movie">
<h2>${movie.title}</h2>
<img onclick="bottonclick()" id="estrelinhas" src="./icons/empty-star.png" alt="estrela vazia" width=40>
<div class="content">
<img src="${movie.posterUrl}" alt="${movie.title}" />
<div class="text">
<p>${movie.summary}</p>
<div class="year">${movie.year}</div>
<div><strong>Directors:</strong> ${movie.director}</div>
<div><strong>Actors:</strong> ${movie.actors}</div>
</div>
</div>
</div>`;
And this is the arrow function I used to make the star change:
const bottonclick = () => {
if (document.getElementById("estrelinhas").src.includes("empty-star.png")) {
document.getElementById("estrelinhas").src = "./icons/filled-star.png";
} else {
document.getElementById("estrelinhas").src = "./icons/empty-star.png";
}
};
ID attributes of HTML elements should be unique. If you don't have unique ID's the code doesn't know which star to update. Read more about IDs here: https://www.w3schools.com/html/html_id.asp
To fix this, a solution would be to use a unique identifier for each image, so that when you click "favourite" it knows which star to reference.
Assuming for example that the movie.posterURL is unique you can use that as the ID, however the data from wherever you are getting the movie from might already have a unique identifier that you could pass to the id attribute of the image instead
Your code could look something like this:
const getMovieHtml = (movie) => `<div class="movie">
<h2>${movie.title}</h2>
<img onclick="bottonclick(e)" id="${movie.posterUrl}" src="./icons/empty-star.png" alt="estrela vazia" width=40>
<div class="content">
<img src="${movie.posterUrl}" alt="${movie.title}" />
<div class="text">
<p>${movie.summary}</p>
<div class="year">${movie.year}</div>
<div><strong>Directors:</strong> ${movie.director}</div>
<div><strong>Actors:</strong> ${movie.actors}</div>
</div>
</div>
</div>`;
const buttonClick = (e) => {
const star = document.getElementById(e.target.id);
if (star.src.includes("empty-star.png")) {
star.src = "./icons/filled-star.png";
} else {
star.src = "./icons/empty-star.png";
}
}
beacuse you getElementById, so you not take all containers,
but you get 1, first,
change getElementById for querySelectorAll and give there some id,
to localizate in DOM ,
buttonClick(e)
and in
function buttonClick(e) {
e.target.value/id/or something
}

How to track changes of a referenced element with React?

I have a problem which requires me to store the texted of a referenced element in an array.
Now, I first want to display the text for each element(paragraph element with "ebookName" class) in the console, before storing it in the array.
But I have been having problems... Whenever I click an element, the console just logs the previous elements text always. I want for each paragraph element to log that specific elements text, not the previous one
Link to JS code:
import React from 'react'
import "./Styles/Ebook.css"
import { useRef } from 'react';
function Ebook() {
const bookName = useRef();
let ebookData = JSON.parse(sessionStorage.getItem("ebook"));
/*function that displays the specific text of a specific element onto the console*/
const elementLogFunction = () =>{
console.log(bookName.current)
}
return (
<section id="musicRender">
{ebookData.results.map((ebook, i)=>{
return (
<div key={i} className='ebookContentContainer'>
<div className="ebookPicture">
<img src={ebook.artworkUrl100} alt={ebook.trackName} />
</div>
<div className="ebook-description">
<p className="ebookAuthor">Author: {ebook.artistName}</p>
<p ref={bookName} className='ebookAName'>Book Name: {ebook.trackName}</p>
<p className="price">Price: R{(ebook.price * 15.36).toFixed(0)}</p>
<button onClick={elementLogFunction} className="favourites-btn">Add To Favourites</button>
</div>
</div>)
})}
</section>
)
}
export default Ebook
According to your code, ref is only referred to the same data, and the new one will override the old one. In your case, the last book data will be kept.
If you want to have individual book data separately, you can pass a param to elementLogFunction.
You also shouldn't read sessionStorage every rendering. This behavior causes a performance issue due to getting data multiple times. You can use useEffect to read data only once after the first rendering.
function Ebook() {
const [ebookData, setEbookData] = React.useState([]);
//only add data for the first rendering
useEffect(() => {
setEbookData(JSON.parse(sessionStorage.getItem("ebook")));
}, []);
/*function that displays the specific text of a specific element onto the console*/
const elementLogFunction = (ebook) =>{
console.log(ebook.trackName)
}
return (
<section id="musicRender">
{ebookData.results.map((ebook, i)=>{
return (
<div key={i} className='ebookContentContainer'>
<div className="ebookPicture">
<img src={ebook.artworkUrl100} alt={ebook.trackName} />
</div>
<div className="ebook-description">
<p className="ebookAuthor">Author: {ebook.artistName}</p>
<p ref={bookName} className='ebookAName'>Book Name: {ebook.trackName}</p>
<p className="price">Price: R{(ebook.price * 15.36).toFixed(0)}</p>
<button onClick={() => elementLogFunction(ebook)} className="favourites-btn">Add To Favourites</button>
</div>
</div>)
})}
</section>
)
}
export default Ebook

How to render html element in pre / REACT

Hello I am working on a blog post creation tool and I need a engine when I write in textarea <hr/> I get a line, or when I write <img/> I get an image but it doesn't render.
The post is written in a textarea and should be displayed in a div.
How to do it?
const PostCreate = () => {
const [postValue, changeValue] = useState('')
const handleChangeValue = (e:any) => {
changeValue(e.target.value)
console.log(postValue);
}
return (
<div className='postCreate'>
Create New Post
<textarea onChange={handleChangeValue} value={postValue}/>
<div>
{postValue}
</div>
</div>
)
}
If I write <hr/> I get the string <hr/> instead of a line:
You can use dangerouslySetInnerHTML prop for the div.
<div dangerouslySetInnerHTML={{ __html: postValue }} />
You can check it on this document.

Replace blank div card/panel with another one that is clicked in React

I'm new to react and not sure I'm going about this the right way. What is happening is I'm grabbing data from pokemon api turning that data into cards that show up based on game selected. Data gets transferred via prop pokedex. Clicking on a card gets me the information for later storing/use.
Currently I can click the generated cards(from panelComp) and have only 1 show up. It does change based on what I click but does not replace the blank card. I know I will need a statement that stores the card in each div and wont let you go over 6.
What end goal and looking to do is to have 6 blank cards/panels up top as "empty"(grey boxes). I want to fill these with the selected pokemon cards/panels from the ones generated from PanelComp.
Later will be using the selected cards to make a filter based on pokemon types. I know I will also need to add a click event to those cards so I can remove them back to blank. I have tried a few things to no avail any direction would be greatly appreciated as I just cant grasp this for some reason.
Code has placeholders via div emptyBox just so I can lay it out.
import Panel from './Panel';
import './PanelList.css';
const PanelList = ({ pokedex }) => {
const [card, setCard] = useState(null);
const [panelPick, setPanelPick] =useState(null);
const [isSelected, setIsSelected]= useState(false);
useEffect(() => {
setPanelPick(panelComp[card]);
setIsSelected(true);
},[card]);
const panelComp = pokedex.map((pokemon, i) => {
return <Panel
onChange={num => setCard(num)}
panelId={i}
id={pokedex[i].id}
name={pokedex[i].name}
types={pokedex[i].types}
/>
})
const isItSelected = isSelected;
return (
<div>
<div>
<div>
{isItSelected ? (
<div id='Block1'>{panelPick}</div>
) : (
<div className='emptyBox' id='Block1'></div>
)}
</div>
<div className='emptyBox' id='Block2'>{panelPick}</div>
<div className='emptyBox' id='Block3'></div>
<div className='emptyBox' id='Block4'></div>
<div className='emptyBox' id='Block5'></div>
<div className='emptyBox' id='Block6'></div>
</div>
<div>
{panelComp}
</div>
</div>
);
}
export default PanelList;
import React from 'react';
import './Panel.css'
const Panel = ({id, name, types, panelId, onChange, onChildEvent }) => {
const handleEvent = event => {
onChange(panelId)
};
return (
<div className="PNL" onClick={handleEvent}>
<img className='pokeImg' alt='pokemon img' src={`https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/${id}.png`}/>
<div className='Potext'>
<h2>{name[0].toUpperCase() + name.substring(1)}</h2>
<p>Id: {id}</p>
<p>Type: {
types.map(type =>
type.type.name[0].toUpperCase() + type.type.name.substring(1))
.join(', ')
}</p>
</div>
</div>
);
}
export default Panel;
This is my current code for the component and child in question. I can add more if needed from the parent. Not sure if I'm making this too difficult or not. I have been working at this for a few days and just don't know where to go with it. Really trying to learn as I go which I'm not sure if it helps.
Thank you

Puppeter delete node inside element

I want to scrape a page with some news inside.
Here it's an HTML simplified version of what I have :
<info id="random_number" class="news">
<div class="author">
Name of author
</div>
<div class="news-body">
<blockquote>...<blockquote>
Here it's the news text
</div>
</info>
<info id="random_number" class="news">
<div class="author">
Name of author
</div>
<div class="news-body">
Here it's the news text
</div>
</info>
I want to get the author and text body of each news, without the blockquote part.
So I wrote this code :
let newsPage = await newsPage.$$("info.news");
for (var news of newsPage){ // Loop through each element
let author = await news.$eval('.author', s => s.textContent.trim());
let textBody = await news.$eval('.news-body', s => s.textContent.trim());
console.log('Author :'+ author);
console.log('TextBody :'+ textBody);
}
It works well, but I don't know how to remove the blockquote part of the "news-body" part, before getting the text, how can I do this ?
EDIT : Sometimes there is blockquote exist, sometime not.
You can use optional chaining with ChildNode.remove(). Also you may consider innerText more readable.
let textMessage = await comment.$eval('.news-body', (element) => {
element.querySelector('blockquote')?.remove();
return element.innerText.trim();
});

Categories