I want to find all the children of a g node with value of id attribute composed by:
a[number]-[one or more chars]
// examples:
// - id="a1-a"
// - id="a1-b"
// - id="a1-abcd"
// - id="a10-f"
// - id="a0-z"
// - id="b1-a" // not valid
// - id="a1-2" // not valid
so I tried:
const items = gElement.querySelectorAll(`[id^='a[0-9]+-[a-zA-Z]+']`)
But, it doesn't work.
In your query selector, the pattern you're using ([0-9]+) is not being interpreted as a regular expression. Use the RegExp constructor to create a regular expression object from a string :
const regex = new RegExp('^a[0-9]+-[a-zA-Z]+$');
const parentElement = document.querySelector('#parent-element');
const items = parentElement.querySelectorAll(`[id]`);
const children = Array.from(items).filter(item => regex.test(item.id));
console.log(children);
<div id="parent-element">
<p id="a1-a">Child 1</p>
<p id="a1-b">Child 2</p>
<p id="INVALID-1">Child 3</p>
<p id="a10-f">Child 4</p>
<p id="INVALID-2">Child 5</p>
<p id="b1-a">Child 6</p>
<p id="a1-2">Child 7</p>
</div>
Related
How, using vanilla JavaScript can i most efficiently (and elegantly) select all elements between two other specified elements? For example, how could I pick all elements between:
<h2> and <hr> .. which would result in an [ p, p, ul ] ... notice the <p>this should not be found</p> should not be "found" since its there is no after it.
I know i can write a bunch of JS to check siblings, and iterate through - but im hoping there is a more elegant way im not seeing. I'd be find if there was some querySelectorAll(..) selector machination i could use as well (or like an intersection of 2 invocations) ..
<div>
<h1>Title</h1>
<h2>Subitle</h2>
<p>Para 1 link</p>
<p>Para 2</p>
<ul><li>A list</li></ul>
<hr/>
<h2>Another subtitle</h2>
<p>this should not be found</p>
</div>
Here's a couple of possible solutions:
const getChildrenInRange1 = (parent, firstChildSelector, lastChildSelector) => {
const res = [];
let current = parent.querySelector(firstChildSelector)?.nextElementSibling;
while (current) {
if (current.matches(lastChildSelector)) {
return res;
}
res.push(current);
current = current.nextElementSibling;
}
return [];
}
const getChildrenInRange2 = (parent, firstChildSelector, lastChildSelector) => {
const afterLast = parent
.querySelectorAll(`${firstChildSelector} ~ ${lastChildSelector} ~ *`);
if (afterLast.length === 0) {
return [];
}
const afterFirstWithoutLast = parent
.querySelectorAll(`${firstChildSelector} ~ *:not(${lastChildSelector})`);
return _.difference(afterFirstWithoutLast, afterLast);
}
const container = document.getElementById('container');
console.log(getChildrenInRange1(container, 'h2', 'hr'));
console.log(getChildrenInRange2(container, 'h2', 'hr'));
<script src="https://cdn.jsdelivr.net/npm/lodash#4.17.21/lodash.min.js"></script>
<div id="container">
<h1>Title</h1>
<h2>Subitle</h2>
<p>Para 1 link</p>
<p>Para 2</p>
<ul><li>A list</li></ul>
<hr/>
<h2>Another subtitle</h2>
<p>this should not be found</p>
</div>
I'm trying to get some text using Cheerio that is placed after a single <br> tag.
I've already tried the following lines:
let price = $(this).nextUntil('.col.search_price.discounted.responsive_secondrow').find('br').text().trim();
let price = $(this).nextUntil('.col.search_price.discounted.responsive_secondrow.br').text().trim();
Here is the HTML I'm trying to scrape:
<div class="col search_price_discount_combined responsive_secondrow" data-price-final="5039">
<div class="col search_discount responsive_secondrow">
<span>-90%</span>
</div>
<div class="col search_price discounted responsive_secondrow">
<span style="color: #888888;"><strike>ARS$ 503,99</strike></span><br>ARS$ 50,39
</div>
</div>
I would like to get "ARS$ 50,39".
If you're comfortable assuming this text is the last child element, you can use .contents().last():
const cheerio = require("cheerio"); // 1.0.0-rc.12
const html = `
<div class="col search_price_discount_combined responsive_secondrow" data-price-final="5039">
<div class="col search_discount responsive_secondrow">
<span>-90%</span>
</div>
<div class="col search_price discounted responsive_secondrow">
<span style="color: #888888;"><strike>ARS$ 503,99</strike></span><br>ARS$ 50,39
</div>
</div>
`;
const $ = cheerio.load(html);
const sel = ".col.search_price.discounted.responsive_secondrow";
const text = $(sel).contents().last().text().trim();
console.log(text); // => ARS$ 50,39
If you aren't comfortable with that assumption, you can search through the children to find the first non-empty text node:
// ...
const text = $([...$(sel).contents()]
.find(e => e.type === "text" && $(e).text().trim()))
.text()
.trim();
console.log(text); // => ARS$ 50,39
If it's critical that the text node immediately follows a <br> tag specifically, you can try:
// ...
const contents = [...$(sel).contents()];
const text = $(contents.find((e, i) =>
e.type === "text" && contents[i-1]?.tagName === "br"
))
.text()
.trim();
console.log(text); // => ARS$ 50,39
If you want all of the immediate text children, see:
How to get a text that's separated by different HTML tags in Cheerio
cheerio: Get normal + text nodes
You should be able to get the price by using:
$('.col.search_price.discounted.responsive_secondrow').html().trim().split('<br>')
This gets the inner HTML of the element, trims extra spaces, then splits on the <br> and takes the 2nd part.
See example at https://jsfiddle.net/b7nt0m24/3/ (note: uses jquery which has a similar API to cheerio)
Using document.getElementsByClassName("span3 pickItem").outerHTML) I set a variable htmlData to contain:
<div itemscope="" class="span3 pickItem">
<p itemprop="name" class="name">
<a href="/user/view?id=4943">
<span>John Doe</span>
<br />
<span>'Arizona'</span>
<br />
<span>'Student'</span>
</a>
</p>
</div>
How can I pick each value from the span tag and console.log them as such:
console.log(...span[0]) output: John Doe
console.log(...span[1]) output: Arizona
console.log(...span[2]) output: Student
Could do something like this
let namesArr = [];
let name = document.querySelectorAll("span");
name.forEach(function(names) {
namesArr.push(names.innerHTML);//Stores all names in array so you can access later
});
console.log(namesArr[0]);
console.log(namesArr[1]);
console.log(namesArr[2]);
Something like this should work:
// console log all names
const items = document.querySelectorAll('div.pickItem span')
items.forEach(item => {
console.log(item.innerText)
})
// console each from array index
console.log(items[0].innerText)
console.log(items[1].innerText)
console.log(items[2].innerText)
let spans = document.getElementsByTagName('span');
console.log(spans[0].innerHTML); //'Joe Doe'
You don't even need the htmlData variable because the DOM elements already exist. If you want to learn about parsing a string of HTML (this is what your htmlData variable has in it) into DOM elements, you can reivew DOMParser.parseFromString() - Web APIs | MDN.
Select the anchor
Select its child spans and map their textContent properties
function getTextFromSpan (span) {
// Just return the text as-is:
// return span.textContent?.trim() ?? '';
// Or, you can also remove the single quotes from the text value if they exist:
const text = span.textContent?.trim() ?? '';
const singleQuote = `'`;
const hasQuotes = text.startsWith(singleQuote) && text.endsWith(singleQuote);
return hasQuotes ? text.slice(1, -1) : text;
}
const anchor = document.querySelector('div.span3.pickItem > p.name > a');
const spanTexts = [...anchor.querySelectorAll(':scope > span')].map(getTextFromSpan);
for (const text of spanTexts) {
console.log(text);
}
<div itemscope="" class="span3 pickItem">
<p itemprop="name" class="name">
<a href="/user/view?id=4943">
<span>John Doe</span>
<br />
<span>'Arizona'</span>
<br />
<span>'Student'</span>
</a>
</p>
</div>
I have a function which accepts two parameters, each of type HTML element. It is supposed to return which element appears first in the document order. Is there any simple way to determine this?
Template -
<body>
<div id="div1">
<div id="div2">
</div>
</div>
<div id="div3">
<div id="div4">
</div>
</div>
</body>
JS -
const elem1 = document.getElementById('div2');
const elem2 = document.getElementById('div4');
const firstAppearingElement = checkOrder(elem1, elem2); // it should return elem1
function checkOrder(element1, element2) {
// check which one appears first in dom tree
}
You can try with Node.compareDocumentPosition()
The Node.compareDocumentPosition() method compares the position of the
given node against another node in any document.
The syntax is object.compareDocumentPosition (nodeToCompare);
let first = document.getElementById('a');
let second=document.getElementById('b');
// Because the result returned by compareDocumentPosition() is a bitmask, the bitwise AND operator has to be used for meaningful results.See link above for more
if (first.compareDocumentPosition(second) & Node.DOCUMENT_POSITION_FOLLOWING) {
console.log('element with id a is before element with id b'); //
} else {
console.log('element with id a is after element with id b');
}
<div id="a"></div>
<div id="b"></div>
So, I made a JavaScript function which helps me to define several parts that are hidden at first but can be shown when clicking a link (I suck at explaining, sry... you'll see what I mean when having a look at the code snippet)
The idea that I had then was to make a function that would show the next 'page' (make the id 1 higher) and another function that would go one 'page' back (reduce the id by 1, so to speak).
Here's the JS and HTML code:
function change_page (id) {
var content_raw = document.getElementById(id);
var content = content_raw.innerHTML;
var display = document.getElementById('replace');
display.innerHTML = content;
}
function page_back () {
//function to go one page back
}
function page_forward () {
//function to go one page forward
}
<div id='replace'>
This is page 1 (shown by default)
</div>
<div style='display:none;'>
<p id="1">This is page 1</p>
<p id="2">This is page 2</p>
<p id="3">This is page 3</p>
<p id="4">This is page 4</p>
<p id="5">This is page 5</p>
</div>
<p style="text-align:center; width:100%;">
Back
Page 1 |
Page 2 |
Page 3 |
Page 4 |
Page 5
Forward
</p>
I really hope that you guys can help me because although I can programm quite well in PHP, I hardly can with JS.
Create a helper variable
var currentPage = 1;
Then, in the change_page function you would always update this value
function change_page (id) {
currentPage = id;
...
And the page_back/page_forward would be calling change_page function with either increased or decreased value of currentPage. So page_back could look like this
function page_back() {
change_page(currentPage - 1);
}
Lastly, you need to make sure that the page_back and page_forward will never result in trying to load a page below 1/above the actual number of pages. To secure that, in the change_page function, you could check whether id is not less than 1 and at the same time it is smaller than max number of pages. The check could look like
function change_page (id) {
if (id < 1 || id > 5)
return;
currentPage = id;
...
Just make a global variable to store the current page you're in.
function change_page (id) {
_currentPage = id;
var content_raw = document.getElementById(_currentPage);
var content = content_raw.innerHTML;
var display = document.getElementById('replace');
display.innerHTML = content;
}
function page_back () {
change_page(_currentPage-1);
}
function page_forward () {
change_page(_currentPage+1);
}
// global variable
var _currentPage;
<div id='replace'>
This is page 1 (shown by default)
</div>
<div style='display:none;'>
<p id="1">This is page 1</p>
<p id="2">This is page 2</p>
<p id="3">This is page 3</p>
<p id="4">This is page 4</p>
<p id="5">This is page 5</p>
</div>
<p style="text-align:center; width:100%;">
Back
Page 1 |
Page 2 |
Page 3 |
Page 4 |
Page 5
Forward
</p>
Try This Code
Working JSFIDDLE Here
var curPage = 0;
function change_page (id) {
var content_raw = document.getElementById(id);
curPage = parseInt(id);
var content = content_raw.innerHTML;
var display = document.getElementById('replace');
display.innerHTML = content;
}
function page_back () {
//function to go one page back
if(curPage==1)
return;
var content_raw = document.getElementById(curPage-1);
curPage = curPage-1;
var content = content_raw.innerHTML;
var display = document.getElementById('replace');
display.innerHTML = content;
}
function page_forward () {
if(curPage==5)
return;
var content_raw = document.getElementById(curPage+1);
curPage = curPage+1;
var content = content_raw.innerHTML;
var display = document.getElementById('replace');
display.innerHTML = content;
}
<div id='replace'>
This is page 1 (shown by default)
</div>
<div style='display:none;'>
<p id="1">This is pagse 1</p>
<p id="2">This is paage 2</p>
<p id="3">This is page 3</p>
<p id="4">This is page 4</p>
<p id="5">This is page 5</p>
</div>
<p style="text-align:center; width:100%;">
Back
Page 1 |
Page 2 |
Page 3 |
Page 4 |
Page 5
Forward
</p>
Working Demo Here