parse table row with cheerio - javascript

I try to extract some data from a table with cheerio.
Here is a simplified version:
const table = `
<table>
<tr>
<td></td>
<td class="with-link"></td>
</tr>
</table>
`
const row = `<td></td>`
class Scraper {
htmlToDom(html) {
return cheerio.load(html)
}
findHref(row) {
return row('a').attr('href')
}
}
const scraper = new Scraper()
const cheerioRow = scraper.htmlToDom(row)
console.log(scraper.findHref(cheerioRow))
const cheerioTable = scraper.htmlToDom(table)
cheerioTable('tr').each(function() {
//console.log(this)
let td = this.find('td.with-link')
console.log(scraper.findHref(td))
})
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<script src="https://wzrd.in/standalone/cheerio#latest"></script>
<title>JS Bin</title>
</head>
<body>
</body>
</html>
Scraper class has two methods, one can load the html (string) and returns a cheerio object. The other one expects a (cheerio) td object, and returns the href from it.
The first console log shows that findHref works as expected. Then I load a whole table, loop through its rows (just one in this case). Inside the each loop this should be a roe (tr) object. I try to find the correct cell (with class 'with-link'), and pass it to findHref.
But I am getting
"TypeError: this.find is not a function
Just in case, here is the JsBin: https://jsbin.com/vakofapiro/edit?js,console

Instead of this use cheerioTable.
`let td = cheerioTable(this).find('td.with-link').html();`
It will give you the inner html of td which is a href

Related

How to create table in Node.js script using data from a file?

Node.js script contains following route definition:
app.get('/',function(req,res){
res.writeHead(200,{'Content-Type': 'text/html'});
fs.createReadStream('file.txt').pipe(csv()).on('data',(data)=>result.push(data)).on('end',() =>{
.
.
.
.
let param1 = first;
let param2 = second;
let param3 = third;
let param4 = forth;
.
.
.
res.write(html_table); // the task
res.end();
});
})
Variables param? represent the data extracted from a file necessary for the table. Goal is to make this route return response which will contain html document with a table (or just a table). Let
param1 = "First parameter", param2 = "Second parameter"...
The response should be in following format.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script></script>
<title>Table</title>
</head>
<body>
<table id="myTable" border="1px solid black">
<tr>
<th>Param1</th>
<th>Param2</th>
<th>Param3</th>
<th>Param4</th>
</tr>
<tr>
<td>First parameter</td>
<td>Second parameter</td>
<td>Third parameter</td>
<td>Forth parameter</td>
</tr>
</table>
</body>
</html>
What is the best approach for solving this? Is it using cheerio package, or manipulating with client and server side javascript files?
First you need to create a HTML file in your project folder. I created mine as table.html in directory projectFolder/files/. So the HTML file should look like this:
projectFolder/files/table.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<table>#{tableData}</table>
</body>
</html>
Notice that in our table.html we have #{tableData} text written inside table element. We will replace this #{tableData} text with our redered HTML in the router.
Next step is manipulating this HTML file in your router and send it to the client. To do this, we need file system module.
router.get('/', function(req, res, next) {
// Read HTML file
fs.readFile('./files/table.html', 'utf-8', function(err, data) {
// Redirect to error handler if there's any error
if(err) return next(err);
res.writeHead(200, { 'Content-Type': 'text/html' });
let tableData = {
param1: 'value1',
param2: 'value2',
param3: 'value3'
};
// Write tableData object as a HTML table rows and columns
let tableDataHead = '<tr>';
let tableDataBody = '<tr>';
for(let param in tableData) {
tableDataHead += `<th>${param}</th>`;
tableDataBody += `<td>${tableData[param]}</td>`;
}
tableDataHead += '</tr>';
tableDataBody += '</tr>';
// Concatenate tableDataHead and tableDataBody
let tableDataHTML = tableDataHead + tableDataBody;
// Replace #{tableData} text with our HTML data
data = data.replace('#{tableData}', tableDataHTML);
// Send rendered HTML file to the client
res.write(data);
res.end();
});
});
And the final response sended to client should look like this:
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body cz-shortcut-listen="true">
<table>
<tr>
<th>param1</th>
<th>param2</th>
<th>param3</th>
</tr>
<tr>
<td>value1</td>
<td>value2</td>
<td>value3</td>
</tr>
</table>
</body>
</html>
Well the question includes a lot of code so I will give you a short set of steps that can help.
You need to read the file and save into a variable (Looks like you already doing that);
Now you need to use some kind of template engine to render the HTML page. I would advise you to use EJS, as a template engine.
Assuming that you have created an EJS template called csv-table.ejs file you need to call render function to render the data you have.
Let's say you have an array data variable, and you are using a template that is using data variable too, so you need to call response.render('csv-table', { data });
In the template you will need to loop over the data variable and render the table.

Displaying content of xml data in html file is not working

I want to display the content of a XML file into a html file.
I have seen and tried the example shown in the following link
https://www.youtube.com/watch?v=VxKGVb0oOBw
I have created html file copying the exactly the code in that example. Here is the code of my first html file
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset=""UTF-8>
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>Test 1oading xml</title>
</head>
<body>
<div id='content'>
<table id="books" cellpadding="10px" style="text-align:left;">
<thead>
<tr><th>Author</th><th>Title</th>
</tr>
</thead>
<tbody></tbody>
</table>
</div>
<script>
let xmlContent = '';
let tableBooks = document.getElementById('books');
fetch('books.xml').then((response)=>{
response.text().then((xml)=>{
xmlContent = xml;
let parser = new DOMParser();
let xmlDOM = parser.parseFromString(xmlContent, 'appliction/xml');
let books = xmlDOM.querySelectorAll('book');
books.forEach(bookXmlNode => {
let row = document.createElement('tr');
//author
let td = document.createElement('td');
td.innerText = bookXmlNode.children[0].innerHTML;
row.appendChild(td);
//title
let td = document.createElement('td');
td.innerText = bookXmlNode.children[1].innerHTML;
row.appendChild(td);
tableBooks.children[1].appendChild(row);
});
});
});
</script>
</body>
</html>
copied the xml file content from here https://learn.microsoft.com/en-us/previous-versions/windows/desktop/ms762271(v%3Dvs.85) .. saved the file as books.xml in the same folder of the html file. Although Ideally I want to display data from external xml file so that the data can be updated dynamically.
When I open the html file it is not showing the xml data.
I have also tried with the code from this link.
https://www.encodedna.com/2014/07/extract-data-from-an-xml-file-using-javascript.htm
Thant is also not working.
How to display data of an (external) xml file into a html file
Screenshot of inspect page. The top one for the code of the you tube video.
The botom one is for the code from https://www.encodedna.com/2014/07/extract-data-from-an-xml-file-using-javascript.htm
Your code is basically correct but you have a few typos. Try the code below, which works for me. As other commenters have mentioned, you can't just open the file, you need a web server to serve it up. The video you link to does this using Live Server in Visual Studio Code.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="" UTF-8>
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>Test 1oading xml</title>
</head>
<body>
<div id='content'>
<table id="books" cellpadding="10px" style="text-align:left;">
<thead>
<tr>
<th>Author</th>
<th>Title</th>
</tr>
</thead>
<tbody></tbody>
</table>
</div>
<script>
let xmlContent = '';
let tableBooks = document.getElementById('books');
fetch('books.xml').then((response) => {
response.text().then((xml) => {
xmlContent = xml;
let parser = new DOMParser();
let xmlDOM = parser.parseFromString(xmlContent, 'application/xml');
let books = xmlDOM.querySelectorAll('book');
books.forEach(bookXmlNode => {
let row = document.createElement('tr');
//author
let td = document.createElement('td');
td.innerText = bookXmlNode.children[0].innerHTML;
row.appendChild(td);
//title
let td2 = document.createElement('td');
td2.innerText = bookXmlNode.children[1].innerHTML;
row.appendChild(td2);
tableBooks.children[1].appendChild(row);
});
});
});
</script>
</body>
</html>
The typos are: id="books", 'application/xml' and you can't use td as a variable name twice.
By the way, when you have problems like this the first place to look is in the browser's console window. Hit F12 after the browser has launched and failed to show your data, and go to Console if it's not selected: it will show you any errors and where they are coming from. If you're using VS Code you can actually debug the script as well I think, meaning you can single-step through it seeing what's going on.

How to replace text in a html document using Javascript

I have written this code which I thought was correct, but although it runs without error, nothing is replaced.
Also I am not sure what event I should use to execute the code.
The test a simple template for a landing page. The tokens passed in on the url will be used to replace tags or tokens in the template.
<!DOCTYPE html>
<html>
<head>
<title>TODO supply a title</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script>
// gets passed variables frm the url
function getQueryVar(str) {
return 'Newtext'; // JUST SCAFFOLD FOR TESTING
}
function searchReplace() {
/**/
var t = 0;
var tags = Array('keyword', 'locale', 'advert_ID');
if (document.readyState === 'complete') {
var str = document.body.innerText;
for (t = 0; t < tags.length; t++) {
//replace in str every instance of the tag with the correct value
if (tags[t].length > 0) {
var sToken = '{ltoken=' + tags[t] + '}';
var sReplace = getQueryVar(tags[t]);
str.replace(sToken, sReplace);
} else {
var sToken = '{ltoken=' + tags[t] + '}'
var sReplace = '';
str.replace(sToken, sReplace);
//str.replace(/sToken/g,sReplace); //all instances
}
}
document.body.innerText = str;
}
}
</script>
</head>
<body>
<H1> THE HEADING ONE {ltoken=keyword}</H1>
<H2> THE HEADING TWO</H2>
<H3> THE HEADING THREE</H3>
<P>I AM A PARAGRAPH {ltoken=keyword}</P>
<div>TODO write content</div>
<input type="button" onclick="searchReplace('keyword')">
</body>
</html>
So when the documment has finished loading I want to execute this code and it will replace {ltoken=keyword} withe value for keyword returned by getQueryVar.
Currently it replaces nothing, but raises no errors
Your problem is the fact you don't reassign the replacement of the string back to it's parent.
str.replace(sToken,sReplace);
should be
str = str.replace(sToken,sReplace);
The .replace method returns the modified string, it does not perform action on the variable itself.
Use innerHTML instead innerText and instead your for-loop try
tags.forEach(t=> str=str.replace(new RegExp('{ltoken='+ t+'}','g'), getQueryVar(t)))
<!DOCTYPE html>
<html>
<head>
<title>TODO supply a title</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script>
// gets passed variables frm the url
function getQueryVar(str)
{
return'Newtext';// JUST SCAFFOLD FOR TESTING
}
function searchReplace() {
/**/
var t=0;
var tags =Array('keyword','locale','advert_ID');
if (document.readyState==='complete'){
var str = document.body.innerHTML;
tags.forEach(t=> str=str.replace(new RegExp('{ltoken='+ t+'}','g'), getQueryVar(t)));
//tags.forEach(t=> str=str.replace(new RegExp('{ltoken='+ tags[t]+'}', 'g'), getQueryVar(tags[t])));
document.body.innerHTML=str;
}
}
</script>
</head>
<body >
<H1> THE HEADING ONE {ltoken=keyword}</H1>
<H2> THE HEADING TWO</H2>
<H3> THE HEADING THREE</H3>
<P>I AM A PARAGRAPH {ltoken=keyword}</P>
<div>TODO write content</div>
<input type ="button" onclick="searchReplace('keyword')" value="Clicke ME">
</body>
</html>

how to get innerHTML value in javascript code

I want to change table <td> data by using innerHTML property. But after applying innerHTML property those values set in <td> are not accessible in Javascript code.
So is there any alternative to innerHTML property so that value can be set in <td> and it can also be accessed in Javascript Code.
Javascript code
<script>
var row=0,col=0,i=1;//can be used in loop
document.getElementById("tableID").rows[row].cells[col].innerHTML=i;
</script>
Look at this small sample, innerHTML works. Walk with cursor keys through the Table. Show us more Code
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Table key´s</title>
<style>
td{width:40px;height:40px;background:#ddd;}
</style>
</head>
<body>
<div id="tableContainer">
</div>
<script>
var aktRow=aktCol=4,max=9;
tableContainer.innerHTML = '<table id="mt">'+('<tr>'+'<td></td>'.repeat(max+1)+'</tr>').repeat(max+1)+'</table>';
mt.rows[aktRow].cells[aktCol].style.background='#f00';
window.addEventListener("keyup", function(e){
var colDiff, rowDiff;
var keyMap = new Map([[37,[-1,0]],[38,[0,-1]],[39,[1,0]],[40,[0,1]]]);
if (keyMap.has(e.keyCode)){
mt.rows[aktRow].cells[aktCol].style.background='#ddd';
mt.rows[aktRow].cells[aktCol].innerHTML=aktRow+'-'+aktCol;
console.log(mt.rows[aktRow].cells[aktCol].innerHTML);
[colDiff,rowDiff]=keyMap.get(e.keyCode);
aktRow+=rowDiff;
aktCol+=colDiff;
aktRow = (aktRow>max) ? max : (aktRow < 0) ? 0 : aktRow;
aktCol = (aktCol>max) ? max : (aktCol < 0) ? 0 : aktCol;
mt.rows[aktRow].cells[aktCol].style.background='#f00';
}
})
</script>
</body>
</html>
your code is wrong here
.rows[row].cells[col]
This is what i suggest:
set an id for each cell, something like col1row1 as id, then access the cell by id:
document.getElementById("col1row1").innerHTML = i
or have a for loop go through each row and cell with getElementsByType('td').innerHTML = i for example
take a look at this :
Iterating through a table with JS

document.getElementById("#foo"); returns null

I am trying to create a media player.
I am not sure why my call to document.getElementById("#playlist-table"); returns null when I have in my HTML <table id="playlist-table"></table>. I have tried running the script just before the </body> tag to make sure the DOM was ready, and still no luck. Here is my code:
HTML(shortened)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8"/>
<title>JAMTRACKS</title>
<script type="text/javascript" src="js.js"></script>
</head>
<body>
<div id="media-player">
<div id="playlist">
<table id="playlist-table"></table>
</div>
</div>
</body>
</html>
JS(shortened)
function addTrack(title){
var table = document.getElementById("#playlist-table");
var tr = document.createElement("tr");
var titleTd = document.createElement("td");
var titleNode = document.createTextNode("title");
table.appendChild(tr); //ERROR IS HERE
tr.appendChild(titleTd);
titleTd.appendChild(titleNode);
...
}
addTrack(Song_Name);
Error : Uncaught TypeError: Cannot read property 'appendChild' of null
Thank you
remove the hashtag # from the selector
var table = document.getElementById("playlist-table");
if you want to keep it, you should use querySelector()
document.querySelector('#playlist-table')
Correct ways to query for id:
document.getElementById('foo');
or
document.querySelectorAll('#foo');
Returns the first element:
document.querySelector('#foo');
^^
var table = document.getElementById("playlist-table");

Categories