How to extract textContent from a list of classes in Puppeteer - css-selectors

In Puppeteer how can I extract the date and list of items purchased on that date? I can extract each class separately with a querySelectorAll([class='date or item']) but don't know how to do it all at one time.
I wish to extract Jan 1, 2022, item-1, item-2, item-3, Feb 1, 2022, item-4, Mar 1, 2022, item-5, item-6.
<ul>
<li>
<div class=”date”>Jan 1, 2022</div>
<div class=”item”>item-1</div>
<div class=”item”>item-2</div>
<div class=”item”>item-3</div>
</li>
<li>
<div class=”date”>Feb 1, 2022</div>
<div class=”item”>item-4</div>
</li>
<li>
<div class=”date”>Mar 1, 2022</div>
<div class=”item”>item-5</div>
<div class=”item”>item-6</div>
</li>
</ul>

I'm not 100% sure if you want a flat array or an array organized by date (the second option seems much clearer to me), but here's both:
// flat array of both classes
console.log(
[...document.querySelectorAll(".date, .item")]
.map(e => e.textContent)
);
// date array of objects
console.log(
[...document.querySelectorAll(".date")].map(e => ({
date: e.textContent,
// or e.parentNode
items: [...e.closest("li").querySelectorAll(".item")]
.map(e => e.textContent)
}))
);
// object keyed by dates, if they're unique
console.log(
[...document.querySelectorAll(".date")].reduce((a, e) => {
a[e.textContent] =
[...e.closest("li").querySelectorAll(".item")]
.map(e => e.textContent)
;
return a;
}, {})
);
<ul>
<li>
<div class="date">Jan 1, 2022</div>
<div class="item">item-1</div>
<div class="item">item-2</div>
<div class="item">item-3</div>
</li>
<li>
<div class="date">Feb 1, 2022</div>
<div class="item">item-4</div>
</li>
<li>
<div class="date">Mar 1, 2022</div>
<div class="item">item-5</div>
<div class="item">item-6</div>
</li>
</ul>
Here's how to scrape the alternate HTML structure with nested image tags based on your new requirements from the comments and this follow-up question:
console.log(
[...document.querySelectorAll(".date, .item")]
.flatMap(e =>
e.classList.contains("item")
? [...e.querySelectorAll("img")]
.map(e => e.getAttribute("alt"))
: e.textContent
)
);
<ul>
<li>
<div class="date">Mar 1, 2022</div>
<div class="item">
<img src="http://image.com/img3.jpg" alt="item-3">
<img src="http://image.com/img5.jpg" alt="item-5">
</div>
</li>
<li>
<div class="date">Mar 3, 2022</div>
<div class="item">
<img src="http://image.com/img2.jpg" alt="item-2">
</div>
</li>
</ul>

Related

Ordered Lists Inside ShieldUI Accordion

Is it possible to include an ordered list inside a ShieldUI accordion? Including one seems to break my accordion; I'm hoping there is some sort of flag or work around to get ordered lists working.
<div class='accordion-container'>
<h2>Delivery Instructions</h2>
<ul id='accordion'>
<li>
<h2>Vehicle Delivery to Media / Client</h2>
<p>Accordion 1</p>
<li>Header
<ol>
<li>One</li>
<li>Two</li>
</ol>
</li>
</li>
<li>
<h2>Delivery to Airport</h2>
<p>Accordion 2</p>
</li>
</ul>
</div>
Try putting it in a DIV, like this:
<div class='accordion-container'>
<h2>Delivery Instructions</h2>
<ul id='accordion'>
<li>
<h2>Vehicle Delivery to Media / Client</h2>
<p>Accordion 1</p>
<li>
<h2>Header</h2>
<div>
<ol>
<li>One</li>
<li>Two</li>
</ol>
</div>
</li>
</li>
<li>
<h2>Delivery to Airport</h2>
<p>Accordion 2</p>
</li>
</ul>
</div>

React: usestate and tab behaviour issue

One of my current projects is learning React and coding in general by analyzing and playing with specific code fragments.
But now I'm stuck. I think I know what the problem is, but I can't solve it.
I have 2 tabs. Tab 1 is set as active, so it's content is shown if i vist the site. Tab 2 is set as inactive. If I click on Tab 2, the state is simply switched. This works.
But if I add a third tab as in this example, the behaviour get's weird.
If I click on Tab 2, Tab 3 is set as active as well and both contents are shown.
I would really appreciate some help. I don't want to give up understanding this.
Thanks!
const [main__tab_state, set_main__tab_state] = useState(true);
<div className="main__tab">
<div className="page__flex">
<div className="main__tab--box">
<div className="main__tab--head">
<ul>
<li className={main__tab_state ? "active" : ""}>
<a className="js-tab" data-tab="tab" onClick={() => set_main__tab_state(true)}>TAB NAME 1</a>
</li>
<li className={main__tab_state ? "" : "active"}>
<a className="js-tab" data-tab="tab1" onClick={() => set_main__tab_state(false)}>TAB NAME 2</a>
</li>
<li className={main__tab_state ? "" : "active"}>
<a className="js-tab" data-tab="tab2" onClick={() => set_main__tab_state(false)}>TAB NAME 3</a>
</li>
</ul>
</div>
</div>
<div className="main__tab--box">
<div className="main__tab--body js-tab__body">
<div id="tab" className={`main__tab--item${main__tab_state ? "active" : ""}`}>
<div className="text">
<p>
TAB TEXT 1
</p>
</div>
</div>
<div id="tab1" className={`main__tab--item${main__tab_state ? "" : "active"}`}>
<div className="text">
<p>
TAB TEXT 2
</p>
</div>
</div>
<div id="tab2" className={`main__tab--item${main__tab_state ? "" : "active"}`}>
<div className="text">
<p>
TAB TEXT 3
</p>
</div>
</div>
</div>
</div>
</div>
</div>
Use const [activeTabIndex, setActiveTabIndex] = useState(0); instead.
Then just kodify your code like so:
<a className="js-tab" data-tab="tab" onClick={() => setActiveTabIndex(0)}>TAB NAME 1</a>
<div id="tab" className={`main__tab--item${activeTabIndex === 0 ? "active" : ""}`}>
First of all you need to change your state.
const [main__tab_state, set_main__tab_state] = useState("tab1"); // change state to store string values and default we will store tab1
Change your code to render tabs and content like below:-
<div className="main__tab">
<div className="page__flex">
<div className="main__tab--box">
<div className="main__tab--head">
<ul>
<li className={main__tab_state === "tab1" ? "active" : ""}>
<a className="js-tab" data-tab="tab" onClick={() => set_main__tab_state("tab1")}>TAB NAME 1</a>
</li>
<li className={main__tab_state === "tab2" ? "" : "active"}>
<a className="js-tab" data-tab="tab1" onClick={() => set_main__tab_state("tab2")}>TAB NAME 2</a>
</li>
<li className={main__tab_state === "tab3" ? "" : "active"}>
<a className="js-tab" data-tab="tab2" onClick={() => set_main__tab_state("tab3")}>TAB NAME 3</a>
</li>
</ul>
</div>
</div>
<div className="main__tab--box">
<div className="main__tab--body js-tab__body">
<div id="tab" className={`main__tab--item${main__tab_state === "tab1" ? "active" : ""}`}>
<div className="text">
<p>
TAB TEXT 1
</p>
</div>
</div>
<div id="tab1" className={`main__tab--item${main__tab_state === "tab2" ? "" : "active"}`}>
<div className="text">
<p>
TAB TEXT 2
</p>
</div>
</div>
<div id="tab2" className={`main__tab--item${main__tab_state === "tab3" ? "" : "active"}`}>
<div className="text">
<p>
TAB TEXT 3
</p>
</div>
</div>
</div>
</div>
</div>
</div>

React Issue With Conditional Then Map

I have some react code that checks if an array has any elements. If it does, then it should map over the elements and display them. I am able to do the check but the map throws an error, and I do not know why.
Here is the function:
function MetalOptions() {
const metals_options = GetsOptions(product,"Metal");
if (metals_options.length > 0) {
return(
<React.Fragment>
<h1>yes</h1>
<ul>
{metals_options.map((option,index) =>
<li key={index}>{option}</li>
)}
</ul>
</React.Fragment>
);
}
}
Here is the entire render function:
render() {
const { isLoading, productID, product} = this.state;
function GetsOptions(product,kind) {
const variant_metals = [];
product.variants.map((variant) =>
variant.option_values.map((option) =>
variant_metals.push({type: option.option_type_name,value: option.presentation})
)
);
const filter_variant_metals = variant_metals.filter(item => item.type === kind);
const output = [...new Set(filter_variant_metals.map(s => JSON.stringify(s)))].map(s => JSON.parse(s));
return output;
}
function GetsVariantImages(product) {
const variant_images = [];
product.variants.map((variant) =>
variant.images.map((image) =>
variant_images.push({
original: image.product_url,
thumbnail: image.small_url,
description: image.alt,
originalAlt: image.alt,
thumbnailAlt: image.alt
})
)
);
const output4 = [...new Set(variant_images.map(s => JSON.stringify(s)))].map(s => JSON.parse(s));
return output4;
}
if (isLoading) {
return <div className="sweet-spinner">
<BounceLoader
sizeUnit={"px"}
size={30}
color={"#494847"}
loading={isLoading}
/>
</div>
}
function MetalOptions() {
const metals_options = GetsOptions(product,"Metal");
const metals_options_array = Object.values(metals_options);
if (metals_options.length > 0) {
return(
<React.Fragment>
<h1>yes</h1>
<ul>
{metals_options_array.map((option,index) =>
<li key={index}>{option}</li>
)}
</ul>
</React.Fragment>
);
}
}
const size_options = GetsOptions(product,"Center Diamond Size");
const shape_options = GetsOptions(product,"Center Diamond Shape");
return (
<div>
<div className="container">
<div className="row">
<div className="col-md-7">
{product.master.images.length >= 3?
<section className="main-image-grid">
{product.master.images.slice(0,3).map(image =>
<img key={image.id} src={image.large_url} alt={image.alt}/>
)}
</section>:
<section className="main-image-grid">
{product.master.images.slice(0,1).map(image =>
<img key={image.id} src={image.large_url} alt={image.alt}/>
)}
</section>
}
</div>
<div className="col-md-5 gradient-silver">
<h1 className="text-center">{product.name}</h1>
<p className="text-center">Your Price {product.display_price}</p>
<div className="cta">
<a href={"sms:+19137258268&body=Hello!%20I%20want%20more%20information%20on%20"+product.name}>Text For More Info!</a>
Call To Take A Look
</div>
<h2>Select Your Options</h2>
<MetalOptions />
<p>This comes in various options listed below:</p>
{GetsOptions(product,"Metal").length > 0 &&
<h4>yes</h4>
}
{GetsOptions(product,"Center Diamond Shape").length > 0 &&
<h2>
You have Center Diamond Shape {GetsOptions(product,"Center Diamond Shape").length} Options.
<ul>
</ul>
</h2>
}
{GetsOptions(product,"Center Diamond Size").length > 0 &&
<h2>
You have Center Diamond Size {GetsOptions(product,"Center Diamond Size").length} Options.
</h2>
}
</div>
</div>
<hr className="style-two"/>
<ul className="nav justify-content-center" id="productTab" role="tablist">
<li className="nav-item">
<a className="nav-link active" id="center-tab" data-toggle="tab" href="#center" role="tab" aria-controls="center" aria-selected="false">Center Gemstones</a>
</li>
<li className="nav-item">
<a className="nav-link" id="description-tab" data-toggle="tab" href="#description" role="tab" aria-controls="description" aria-selected="true">Description</a>
</li>
<li className="nav-item">
<a className={"nav-link " + (product.product_properties.length < 0 && 'disabled')} id="properties-tab" data-toggle="tab" href="#properties" role="tab" aria-controls="properties" aria-selected="false">Properties</a>
</li>
</ul>
<div className="tab-content" id="myTabContent">
<div className="tab-pane fade show active" id="center" role="tabpanel" aria-labelledby="center-tab">
<h3 className="h5 text-uppercase text-center">The Center</h3>
<p class="h6 text-center text-muted"><small>Deciding on a center diamond or gemstone is the <span className="text-uppercase font-weight-bold">important decision.</span> The center diamond holds the sentiment and value. If your piece of jewelry is a play, the mounting is a stage, and the center is the actors and script.</small></p>
<p class="h6 text-center text-muted"><small>We have helped so many in Kansas City find beautiful diamonds or gemstones in all qualities and price ranges usually complete with a GIA grading report. We would be honored to help you to your perfect one.</small></p>
<DiamondLinks />
<GemLinks />
</div>
<div className="tab-pane fade" id="description" role="tabpanel" aria-labelledby="description-tab"><p className="text-center">{product.description}</p></div>
<div className="tab-pane fade" id="properties" role="tabpanel" aria-labelledby="properties-tab">
{product.product_properties.map((property,index) =>
<p><span className="font-weight-bold">{property.property_name}:</span> {property.value}</p>
)}
</div>
</div>
</div>
<hr className="style-two"/>
<h2>More Pictures</h2>
<ImageGallery items={GetsVariantImages(product)} lazyLoad={true} showThumbnails={true} thumbnailPosition={'left'}/>
</div>
);
}
}
Any ideas? Thank you for your help!

How to refactor code for switch statement in React?

I want to refactor code for switch statement in React.
I have the data as array of objects. I loop through each object and check its type field. For each type type field I have some message to print and each object data should be in a list item. To do so, I use switch statement and doing so I feel that the code is repeated too much.
How can I rewrite the code below?
render = () => {
switch(item.type) {
case 'item_created':
return (
<li className="item">
<div className="details">
<Svg4/>
<span>{item.type}</span>
</div>
<time>{item.timestamp}</time>
</li>
);
case 'item_deleted':
return (
<li className="item">
<div className="details">
<Svg1/>
<span>{item.name}</span>
</div>
<time>{item.timestamp}</time>
</li>
);
case 'settings_updated':
return (
<li className="item">
<div className="details">
<Svg2/>
<span>{item.version}</span>
</div>
<time>{item.timestamp}</time>
</li>
);
default:
return null;
}}
Could someone help me refactoring this, or provide a better way of doing this?
Refactoring is always kind of subjective, but what about creating a new component that you can reuse multiple times in your switch statement.
const Item = ({icon, text, timestamp}) => (
<li className="item">
<div className="details">
{icon}
<span>{text}</span>
</div>
<time>{timestamp}</time>
</li>
)
Hey You can use object mapping like below.
const mapping = {
item_created: item => (
<li className="item">
<div className="details">
<Svg4 />
<span>{item.type}</span>
</div>
<time>{item.timestamp}</time>
</li>
),
item_deleted: item => (
<li className="item">
<div className="details">
<Svg1 />
<span>{item.name}</span>
</div>
<time>{item.timestamp}</time>
</li>
),
settings_updated: item => (
<li className="item">
<div className="details">
<Svg2 />
<span>{item.version}</span>
</div>
<time>{item.timestamp}</time>
</li>
),
};
Now you can use above object as following
mapping[item.type](item)

why ng-repeat stop working at level 3

I am using angularjs to render a hierarchical structure. The test case is at http://jsbin.com/agodoj/1/edit
My question is why ng-repeat stop working at level 3 ? Thanks
Here is my model
function Test($scope) {
$scope.cars = {
chrylser: {
nameplates: [{
name: "np1",
trims: [
"tirm1", "trim2"
]
}]
}
};
}
Here is my template
<div ng-app ng-controller="Test">
<div ng-repeat="(key, value) in cars">
{{key}}
<ul>
<li ng-repeat="np in value.nameplates">{{np.name}}, {{np.trims}}</li>
<ul>
<li ng-repeat="trim in np.trims">
(ng-repeat stop working here) {{trim}}
</li>
</ul>
</ul>
</div>
</div>
Just need to move the closing </li> tag after your inner <ul>. You had the <li ng-repeat="np in value.nameplates"> immediately closing, ending the loop.
<div ng-app ng-controller="Test">
<div ng-repeat="(key, value) in cars">
{{key}}
<ul>
<li ng-repeat="np in value.nameplates">{{np.name}}, {{np.trims}}
<ul>
<li ng-repeat="trim in np.trims">
works! {{trim}}
</li>
</ul>
</li>
</ul>
</div>
</div>

Resources