Cheerio unmatched selector error while selecting plain text - javascript

I'm scraping a web page with cheerio's .map method. The page's html code looks like this:
<div class="foo">
<h1>Lorem</h1>
<p>Lorem ipsum dolor sit amet.</p>
TEXT WITHOUT TAG
<p>Lorem ipsum dolor sit amet.</p>
</div>
Here is what I do:
let $ = cheerio.load(body);
let contentHtml = $('foo').html();
$(contentHtml).map((index, element) => {
console.log(element);
});
When .map see the 'TEXT WITHOUT TAG', it throws an error like this:
Unmatched selector: ...
Which is expected because it hasn't any selectors. I want to wrap that plain text with <p> tags but I couldn't figure out how.

Your element has class foo and selector not:
let contentHtml = $('.foo').html();

Related

How do I get a class as a string and also the innerHTML from an element within S.fn.init?

S.fn.init [div.definitionAgelenidae, prevObject: S.fn.init(558)]
I have tried:
var Test02 = $(".definition").children().filter(function () {
return $(this).css("visibility") == "visible";
};
console.log(Test02[0]);
I would like to get the string definitionAgelenidae
Directly accessing $.fn.init is not a good idea, and should be avoided. Given the goal you describe in the comments (copied below) there is an alternative.
I would like to get the innerhtml of the visible element so I can have a more specific search result
In this case you can supply another condition in the function you provide to filter() which checks for the presence of a specific word. Something like this:
let term = 'dolor';
let $matches = $(".definition").children().filter(function() {
return $(this).is(":visible") && $(this).text().toLowerCase().includes(term.toLowerCase());
}).addClass('match');
.match { color: #C00; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="definition">
<p>Lorem ipsum dolor sit</p>
</div>
<div class="definition">
<p>Ipsum dolor sit amet</p>
</div>
<div class="definition">
<p>Foo bar fizz buzz</p>
</div>
<div class="definition">
<p>Dolor sit amet consectetur</p>
</div>
Note that .toLowerCase() is used here to make the search case-insensitive. This can be removed if it's not required.

Replacing HTML footer content with JavaScript

I'm trying to replace my footer's text with "Hello World" and I do not want to edit the HTML by adding a class or an id
HTML:
<footer>
<div>
Lorem ipsum dolor sit amet, consectetur adipisicing elit.
</div>
</footer>
JavaScript:
document.getElementById(footer).innerHTML="Hello World";
The problem is that, when I do the code above, nothing is changing
footer is not the id of the element you are selecting, its the tag name.
You can use tag selector for selecting footer.
And to change the div content(i am assuming you want to change the text, keeping div as is), you can select div using the tag selector
and can change the text.
document.getElementsByTagName("footer")[0].getElementsByTagName("div")[0].innerHTML = "Hello World";
Above statement is broken down :
document.getElementsByTagName("footer") //select footer
document.getElementsByTagName("footer")[0] //1st matched element
document.getElementsByTagName("footer")[0].getElementsByTagName("div") // select div
document.getElementsByTagName("footer")[0].getElementsByTagName("div")[0] // first div
document.getElementsByTagName("footer")[0].getElementsByTagName("div")[0].innerHTML = "Hello World"; //change content
document.getElementsByTagName("footer")[0].getElementsByTagName("div")[0].innerHTML = "Hello World";
<footer>
<div>
Lorem ipsum dolor sit amet, consectetur adipisicing elit.
</div>
</footer>
It's because you are selecting id which doesn't exist, try this instead:
document.querySelector('footer').innerHTML = "Hello world";
#edit
document.querySelector('footer').innerHTML = "Hello world";
<footer>
<div>
Lorem ipsum dolor sit amet, consectetur adipisicing elit.
</div>
</footer>
There are a couple of ways by which you can achieve the desired:
1) If you need to change the HTML, you should use the below code for targeting the footer:
document.getElementsByTagName('footer')[0].innerHTML = '<div>Hello World</div>';
document.getElementsByTagName('footer')[0].innerHTML = '<div>Hello World</div>';
<footer>
<div>
Lorem ipsum dolor sit amet, consectetur adipisicing elit.
</div>
</footer>
2) If you wish to just modify the text, without changing the HTML, you can also make use of the following:
document.getElementsByTagName('footer')[0].innerText = 'Hello World';
document.getElementsByTagName('footer')[0].innerText = 'Hello World';
<footer>
<div>
Lorem ipsum dolor sit amet, consectetur adipisicing elit.
</div>
</footer>
The difference between the two approaches is that the former keeps the text inside the div while the latter keeps inside footer tag itself.
If you look through inspection in the two, it will have an output like the below:
1)
<footer>
<div>
Hello World
</div>
</footer>
2)
<footer>
Hello World
</footer>

How to shrink element by clicking on it or either of its siblings - vanilla js (may be useful for styling effects or accordion)

I have three panels, one by another in a row. When I click on one of them, its width increases. When I click on any of other two, the panel which previously increased width shrinks and the newly clicked panel widens. However, I would like to be able to shrink the just widened panel by clicking on it. I am struggling to find the solution but with no effect.
This is my code:
var panels = document.querySelectorAll('.panel'),
activePanel = document.querySelectorAll('.panel.open');
function toggleOpen() {
panels.forEach(function(item) {
if (item.classList.contains('open')) {
item.classList.remove('open');
item.classList.add('closed');
}
});
this.classList.remove('closed');
this.classList.add('open');
}
function closeActivePanel() {
if (activePanel.length > 0) {
activePanel.removeClass('open');
activePanel.addClass('closed');
}
}
panels.forEach(function(panel) {
panel.addEventListener('click', toggleOpen);
});
activePanel.forEach(function(aPanel) {
aPanel.addEventListener('click', closeActivePanel);
});
<div class="panel panel1">
<div class="panel__overlay"></div>
<p>Lorem ipsum dolor sit amet</p>
<p>Lorem ipsum</p>
<p>Lorem ipsum dolor sit amet</p>
</div>
<div class="panel panel2">
<div class="panel__overlay"></div>
<p>Lorem ipsum dolor sit amet</p>
<p>Lorem ipsum</p>
<p>Lorem ipsum dolor sit amet</p>
</div>
<div class="panel panel3">
<div class="panel__overlay"></div>
<p>Lorem ipsum dolor sit amet.</p>
<p>Lorem ipsume</p>
<p>Lorem ipsum dolor sit amet</p>
</div>
The function closeActivePanel just does not fire. And there is no error message.
The Time you query the DOM for the .panel.open elements they don't exist. You have to add the listener after you've added the class "open" to the element or implement a toggle logic in the click handler for the .panel elements.
Finally I found the solution for my problem. I think it would be very useful for accordion element for example. The goal was to have the possibility to increase width of one of elements by clicking on it (class 'open' produces this effect). And then to shrink it either by clicking on this element of any of other two beside it. Having the effect of shrinking the wide element by clicking on other two elements was easy. But to add functionality to shrink it by clicking on itself was quite a gymnastics. Event delegation turned out not to be helpful as I needed to know exact target of the event. Here is what I found working:
var panels = document.querySelectorAll('.panel');
function togglePanels(event) {
var target = event.target;
panels.forEach(function(item) {
if(item != target) {
item.classList.remove('open')
};
});
target.classList.toggle('open');
}
panels.forEach(function(panel) {
panel.addEventListener('click', togglePanels);
});

Find the index position of every element starting with `icon-`in a block of text using javascript

What is the best way of looping through a text block to find the index position of every element starting with icon- using javascript or jQuery.
I also want to ignore any <br> tags in the index position calculation.
I have thought about using substring to find the position of the elements.
Here is an example text block
<div class="intro">
Lorem dolor sit<br>
<span class="icon-pin"></span> consectetur<br>
adiposcing elit, sed do <span class="icon-hand"></span> lorem<br>
ipsum dolor sit amet.
</div>
What I want to get out of this is how many characters in (minus white space and tags) each [class^=icon-] is.
For example the first [class^=icon-] is 14 characters in
Thanks
I think this is what your looking for, it will find the index of the spans and ignore br
$(".intro [class^=icon-]").each(function() {
var i = $(".intro *:not(br)").index(this)
console.log(i)
})
Demo
$(".intro [class^=icon-]").each(function() {
var i = $(".intro *:not(br)").index(this)
console.log(i)
})
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="intro">
Lorem dolor sit<br>
<span class="icon-pin"></span> consectetur<br> adiposcing elit, sed do <span class="icon-hand"></span> lorem<br> ipsum dolor sit amet.
</div>
You can achieve it with jquery each like in the example
$('[class^="icon-"]','.intro').each(function(index, element){
console.log(index,element);
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="intro">
Lorem dolor sit<br>
<span class="icon-pin"></span> consectetur<br>
adiposcing elit, sed do <span class="icon-hand"></span> lorem<br>
ipsum dolor sit amet.
</div>
You can use more than 1 classes on a element. So, You can keep using your "icon-" classes and add another one to capture them like "grabber" and now you are good to go. Just find the "grabber" classes with a for loop like;
var y = "number of grabbers";
for(x:0;x<y;x++){
$('.grabber')[x].function.....
}

Javascript reg exp between closing tag to opening tag

How do I select with Regular Expression the text after the </h2> closing tag until the next <h2> opening tag
<h2>my title here</h2>
Lorem ipsum dolor sit amet <b>with more tags</b>
<h2>my title here</h2>
consectetur adipisicing elit quod tempora
In this case I want to select this text: Lorem ipsum dolor sit amet <b>with more tags</b>
Try this: /<\/h2>(.*?)</g
This finds a closing tag, then captures anything before a new opening tag.
in JS, you'd do this to get just the text:
substr = str.match(/<\/h2>(.*?)<h2/)[1];
Regex101
var str = '<h2>my title here</h2>Lorem ipsum <b>dolor</b> sit amet<h2>my title here</h2>consectetur adipisicing elit quod tempora';
var substr = str.match(/<\/h2>(.*?)<h2/)[1].replace(/<.*?>/g, '');
console.log(substr);
//returns: Lorem ipsum dolor sit amet
Try
/<\/h2>((?:\s|.)*)<h2/
And you can see it in action on this regex tester.
You can see it in this example below too.
(function() {
"use strict";
var inString, regEx, res, outEl;
outEl = document.getElementById("output");
inString = "<h2>my title here</h2>\n" +
"Lorem ipsum dolor sit amet <b>with more tags</b>\n" +
"<h2> my title here </h2>\n" +
"consectetur adipisicing elit quod tempora"
regEx = /<\/h2>((?:\s|.)*)<h2/
res = regEx.exec(inString);
console.log(res);
res.slice(1).forEach(function(match) {
var newEl = document.createElement("pre");
newEl.innerHTML = match.replace(/</g, "<").replace(/>/g, ">");
outEl.appendChild(newEl);
});
}());
<main>
<div id="output"></div>
</main>
I added \n to your example to simulate new lines. No idea why you aren't just selecting the <h2> with a querySelector() and getting the text that way.
Match the tags and remove them, by using string replace() function. Also this proposed solution removes any single closure tags like <br/>,<hr/> etc
var htmlToParse = document.getElementsByClassName('input')[0].innerHTML;
var htmlToParse = htmlToParse.replace(/[\r\n]+/g,""); // clean up the multiLine HTML string into singleline
var selectedRangeString = htmlToParse.match(/(<h2>.+<h2>)/g); //match the string between the h2 tags
var parsedString = selectedRangeString[0].replace(/((<\w+>(.*?)<\/\w+>)|<.*?>)/g, ""); //removes all the tags and string within it, Also single tags like <br/> <hr/> are also removed
document.getElementsByClassName('output')[0].innerHTML += parsedString;
<div class='input'>
<i>Input</i>
<h2>my title here</h2>
Lorem ipsum dolor sit amet <br/> <b>with more tags</b>
<hr/>
<h2>my title here</h2>
consectetur adipisicing elit quod tempora
</div>
<hr/>
<div class='output'>
<i>Output</i>
<br/>
</div>
Couple of things to remember in the code.
htmlToParse.match(/(<h2>.+<h2>)/g); returns an array of string, ie all the strings that was matched from this regex.
selectedRangeString[0] I am just using the first match for demo purspose. If you want to play with all the strings then you can just for loop it with the same logic.

Categories

Resources