im try to image for a table and push that link in to array. when i run the code img get an undefined.
how i can fix this problem.
$("body > form > table:nth-child(5) > tbody > tr").each((index, element) => {
if (index === 0) return true;
const tds = $(element).find("td");
const img = $(tds[0]).attr('src'); // undefined
const flight = $(tds[1]).text(); //working
const origin = $(tds[2]).text(); //working
const time = $(tds[3]).text(); //working
const estimted = $(tds[4]).text(); //working
const status = $(tds[5]).text(); //working
console.log(img);
const tableRow = {
flight,
origin,
time,
estimted,
status
};
});
$(tds[0]).attr('src') will be undefined because td's aren't images.
Maybe you wanted: $(tds[0]).find('img').attr('src')
Related
The problem is that every time I click on an element with a state things appear twice. For example if i click on a button and the result of clicking would be to output something in the console, it would output 2 times. However in this case, whenever I click a function is executed twice.
The code:
const getfiles = async () => {
let a = await documentSpecifics;
for(let i = 0; i < a.length; i++) {
var wrt = document.querySelectorAll("#writeto");
var fd = document.querySelector('.filtered-docs');
var newResultEl = document.createElement('div');
var writeToEl = document.createElement('p');
newResultEl.classList.add("result");
writeToEl.id = "writeto";
newResultEl.appendChild(writeToEl);
fd.appendChild(newResultEl);
listOfNodes.push(writeToEl);
listOfContainers.push(newResultEl);
wrt[i].textContent = a[i].data.documentName;
}
}
The code here is supposed to create a new div element with a paragraph tag and getting data from firebase firestore, will write to the p tag the data. Now if there are for example 9 documents in firestore and i click a button then 9 more divs will be replicated. Now in total there are 18 divs and only 9 containing actual data while the rest are just blank. It continues to create 9 more divs every click.
I'm also aware of React.Strictmode doing this for some debugging but I made sure to take it out and still got the same results.
Firebase code:
//put data in firebase
createFileToDb = () => {
var docName = document.getElementById("title-custom").value; //get values
var specifiedWidth = document.getElementById("doc-width").value;
var specifiedHeight = document.getElementById("doc-height").value;
var colorType = document.getElementById("select-color").value;
parseInt(specifiedWidth); //transform strings to integers
parseInt(specifiedHeight);
firebase.firestore().collection("documents")
.doc(firebase.auth().currentUser.uid)
.collection("userDocs")
.add({
documentName: docName,
width: Number(specifiedWidth), //firebase-firestore method for converting the type of value in the firestore databse
height: Number(specifiedHeight),
docColorType: colorType,
creation: firebase.firestore.FieldValue.serverTimestamp() // it is possible that this is necessary in order to use "orderBy" when getting data
}).then(() => {
console.log("file in database");
}).catch(() => {
console.log("failed");
})
}
//get data
GetData = () => {
return firebase.firestore()
.collection("documents")
.doc(firebase.auth().currentUser.uid)
.collection("userDocs")
.orderBy("creation", "asc")
.get()
.then((doc) => {
let custom = doc.docs.map((document) => {
var data = document.data();
var id = document.id;
return { id, data }
})
return custom;
}).catch((err) => {console.error(err)});
}
waitForData = async () => {
let result = await this.GetData();
return result;
}
//in render
let documentSpecifics = this.waitForData().then((response) => response)
.then((u) => {
if(u.length > 0) {
for(let i = 0; i < u.length; i++) {
try {
//
} catch(error) {
console.log(error);
}
}
}
return u;
});
Edit: firebase auth is functioning fine so i dont think it has anything to do with the problem
Edit: This is all in a class component
Edit: Clicking a button calls the function createFileToDb
I think that i found the answer to my problem.
Basically, since this is a class component I took things out of the render and put some console.log statements to see what was happening. what i noticed is that it logs twice in render but not outside of it. So i took the functions out.
Here is the code that seems to fix my issue:
contain = () => {
const documentSpecifics = this.waitForData().then((response) => {
var wrt = document.getElementsByClassName('writeto');
for(let i = 0; i < response.length; i++) {
this.setNewFile();
wrt[i].textContent = response[i].data.documentName;
}
return response;
})
this.setState({
docs: documentSpecifics,
docDisplayType: !this.state.docDisplayType
})
}
As for creating elements i put them in a function so i coud reuse it:
setNewFile = () => {
const wrt = document.querySelector(".writeto");
const fd = document.querySelector("#filtered-docs");
var newResultEl = document.createElement('div');
newResultEl.classList.add("result");
var wrtEl = document.createElement('p');
wrtEl.classList.add("writeto");
fd.appendChild(newResultEl);
newResultEl.appendChild(wrtEl);
}
The firebase and firestore code remains the same.
the functions are called through elements in the return using onClick.
I have a "clients.txt" file where I have a list of emails. I try to run a program for sending emails where I chose a number of emails to use from the file, in that case the number is 2. After I use the two emails I want to overwrite "clients.txt" without them. The problem is when I try to run the code just for one single time every thing is working! but if I make a loop something is wrong. Looking forward to see any help from you guys. Thanks! I add the code bellow. PS: Sorry for my bad english!
function readEmails(){
const fs = require('fs');
clients_list = fs.readFileSync('clients.txt', 'utf8').split('\n');
let filtered = clients_list.filter(function (el) {
return el != null && el != '';
});
return filtered
}
function dump_array(arr, file){
let fs = require('fs');
let file = fs.createWriteStream(file);
file.on('error', function(err) { /* error handling */ });
arr.forEach(function(v) { file.write(v + '\n'); });
file.end();
}
while_var = 0;
while (while_var < 2){
while_var ++;
let all_clients = readEmails();
let selected_clients = [];
if (all_clients.length > 0){
selected_clients = all_clients.splice(0,2);
dump_array(all_clients, 'clients.txt');
console.log(selected_clients);
}else{
console.log('No more clients')
}
}
const fs = require('fs');
function readEmails(){
const clients_list = fs.readFileSync('clients.txt', 'utf8').split('\n');
const filtered = clients_list
// clear false, 0 and undefined too
.filter(el => !!el)
// remove extra spaces and \r symbols
.map(el => el.trim());
return filtered;
}
function dump_array(arr, file){
// Here you need sync method.
fs.writeFileSync(file, arr.join('\n'));
// And here was 'already declared' error in orginal code
}
let while_var = 0;
while (while_var++ < 2){
let all_clients = readEmails();
let selected_clients = [];
if (all_clients.length > 0){
selected_clients = all_clients.splice(0,2);
dump_array(all_clients, 'clients.txt');
console.log(selected_clients);
}else{
console.log('No more clients')
}
}
I am using Cheerio for web scraping, I have used bs4 earlier.
I want to scrape https://rera.kerala.gov.in/rera_project_details this website; in Python to scrape table we can use findall("tr")[0] to get first <tr>.
But how to perform same in Cheerio?
Below is my code:
var request = require('request');
var cheerio = require('cheerio');
var fs = require('fs');
const url = "https://rera.kerala.gov.in/rera_project_details";
const arr = [];
request({method:"GET",url}, function(err, res, body){
if (res.statusCode==200){
let $ = cheerio.load(body);
const getID = $("#block-zircon-content");
const tbody = getID.find('tbody');
tbody.each((i, el)=>{
const ff = $(el).find("tr");
console.log(ff.html());//it returns first tr
//how to get 2 tr so that i can get td of second tr and can inde on td also
})
}}
)
If I loop over it returns all tr , now how to index on each td so that in last column of table I can get a link to get pdf?
Edit
I have reached till here but how to get list of td elements in tr:
const getID = $(".views-table");
const getBody = getID.find("tbody");
const gettr = getBody.find("tr");
const getfirsttr = $.html(gettr[0]);//it gives me first tr
const getfirsttd = getfirsttr.find("td")//does not work
To answer the index question:
$('tr').eq(n)
will give you the nth tr as a cheerio object. and
$('tr')[n]
will give it as a parse5 object
You should be able to use a selector that will give you all the elements from the required table. Once you have the elements you can access their properties, children etc.
const url = "https://rera.kerala.gov.in/rera_project_details";
request({method:"GET",url}, function(err, res, body) {
if (res.statusCode==200) {
let $ = cheerio.load(body);
// Get all td elements from the table.
let tdElements = $("#block-zircon-content tbody tr td").map((i, el)=>{
return el;
}).toArray();
console.log(`<td> list: Found ${tdElements.length} elements..`);
console.log("tdElements[0]:", tdElements[0]);
console.log("tdElements[0]: (html)", $.html(tdElements[0]))
}}
);
To simply find all td elements in the table using .find() we can try:
const trElements = $("#block-zircon-content tbody").find("tr");
const tdElements = trElements.find("td").toArray();
console.log(`first td:`, tdElements[0]);
all right after doing research and and help above from terry i have understood how it works..
all cheerio functions works on selector html not on text..
below is my code in case any other beginner like me is using cheerio and stuck
var request = require('request');
var cheerio = require('cheerio');
var fs = require('fs');
// const { get } = require('request');
// const { EACCES } = require('constants');
const url = "https://rera.kerala.gov.in/rera_project_details";
const arr = [];
request({method:"GET",url}, function(err, res, body){
if (res.statusCode==200){
let $ = cheerio.load(body);
// this is a selector
const getID = $(".views-table");
const getBody = getID.find("tbody");
const gettr = getBody.find("tr");
gettr.each((index, element)=>{
// if i use normal element it will be treated as normal text but children are avaiable
//ON SELECTORS WE CAN APPLY ALL FUNCTIONS
var std = $(element).find("td")
let number = $(std[0]).contents().text();
let ReraNumbers = $(std[1]).contents().text();
let name = $(std[2]).contents().text().trim()
// difference between tohtml and html is $.html retunr html tag
// to html returns html content
})
// const tdElements= gettr.find("td").toArray();
// console.log(tdElements[2].children[0].data.trim())
// let tdElements = $("#block-zircon-content tbody tr td").map((i, el)=>{
// return el;
// }).toArray();
// console.log(`<td> list: Found ${tdElements.length} elements..`);
// console.log("tdElements[0]:", tdElements[0]);
// console.log("tdElements[0]: (html)", $.html(tdElements[0]))
}}
)
I am new to JavaScript. I have a small code that creates list from input and then adds it to an array. I am able to remove one item from the DOM when the item is clicked, but I couldn't remove it from the array.
I tried to use array.splice(item, 1)
lists.addEventListener("click", function (e) {
e.target.closest("li").remove();
userInputArr.splice(item, 1);});
But it removes the entire array sometime, and sometime removes the last item. when I console log the code, it looks like I clicked 3 or 4 times on the list even though I just clicked once. I have no idea what's wrong. this is the entire code:
const lists = document.querySelector(".lists");
const userInput = document.querySelector(".add-note");
const addBtn = document.querySelector(".add-btn");
const item = document.querySelectorAll(".list");
userInputArr = [];
function addNote() {
if (userInput.value < 1) {
return;
}
lists.insertAdjacentHTML(
"afterbegin",
`<li class='list'>${userInput.value}</li>`
);
userInputArr.push(lists);
lists.addEventListener("click", function (e) {
e.target.closest("li").remove();
userInputArr.splice(item, 1);
});
userInput.value = "";
}
addBtn.addEventListener("click", function () {
addNote();
});
Code is totally meaningless
1)
userInputArr.push(lists)
why you push the same element all the time? As lists refers to the first and the only element with class 'lists'?
2)
userInputArr.splice(item, 1)
please watch carefully what splice does? The first argument is number, but you pass a collection of elements with class 'list'. But i camn not even suggest which element should be removed as it contains the same element as i mentioned in first point
3) You do not need this array at all
So right approach is something like this
const lists = document.querySelector(".lists");
// just once create listener, no need to do it each time
lists.addEventListener("click", function (e) {
// if you want to remove clicked item then
if (e.target.tagName === 'LI') e.target.remove();
// but if you want to remove the first one then uncomment line
// if (this.children[0]) this.children[0].remove()
});
const userInput = document.querySelector(".add-note");
const addBtn = document.querySelector(".add-btn");
///////////////////////////////////////////////////
// item is meaninglee here, so delete this line
// const item = document.querySelectorAll(".list");
//////////////////////
// array is useless too, delete this line
// userInputArr = [];
function addNote() {
// check if it is number
if (isNaN(userInput.value) || Number(userInput.value < 1)) {
return;
}
lists.insertAdjacentHTML(
"afterbegin",
`<li class='list'>${userInput.value}</li>`
);
userInput.value = "";
}
addBtn.addEventListener("click", function () {
addNote();
});
const items = (() => {
const _items = {};
let key = 0;
return {
put(value) {
_items[key++] = value;
console.log("Added", this.all());
return key - 1;
},
remove(key) {
delete _items[key++];
console.log("Removed", this.all());
},
all(asArray = true) {
return asArray ? Object.values(_items) : { ..._items
};
}
}
})();
const inputEl = document.querySelector(".input");
const itemsEl = document.querySelector(".items");
const addBtn = document.querySelector(".btn-add");
addBtn.addEventListener("click", () => {
const value = inputEl.value.trim();
if (!value.length) return;
const key = items.put(value);
const li = document.createElement("li");
li.textContent = value;
li.dataset.key = key;
itemsEl.append(li);
inputEl.value = "";
});
itemsEl.addEventListener("click", (e) => {
const li = e.target.closest("li");
items.remove(li.dataset.key);
li.remove();
});
<input type="text" class="input">
<button class="btn-add">Add</button>
<ul class="items"></ul>
Run code & View in full screen.
use shift() userInputArr.shift()
you are also getting double clicks because your addNote() function contains an event listener lists.addEventListener and it's executed by another event listner addBtn.addEventListener you should probably move
lists.addEventListener out of the addNote function
This function is called as a form submit, and further calls a new function for rendering the list of divs. After this is done the website is refreshed because of drag and drop functionality. The problem is that I cant seem to find a way to create an unique ID that persists through page refresh and isnt overwritten on page load because of ex: "let taskId = 0".
Any ideas? :)
function createNewTask(event){
if(document.querySelector("[name='description']").value === "") {
alert("Cannot add empty task.");
} else {
event.preventDefault();
let taskId = 0;
const description = document.querySelector("[name='description']").value;
const givenTo = document.querySelector("[name ='givenTo']").value;
const createdByName = document.querySelector("[name = 'workerName']").value;
const task = {taskId, description, givenTo, createdByName, section: 'task-section'};
const taskList = JSON.parse(window.localStorage.getItem("taskList")) || [];
taskId++;
taskList.push(task);
window.localStorage.setItem("taskList", JSON.stringify(taskList));
// renderTaskList();
renderStoredList();
//Reload page after createNewTask to activate draggable
location.reload();
}
}
Use length to get the next taskId.
function createNewTask(event){
if(document.querySelector("[name='description']").value === ""){
alert("Cannot add empty task.");
} else {
event.preventDefault();
const tasklist = JSON.parse(window.localStorage.getItem("taskList")) || []
let taskId = tasklist.length;
const description = document.querySelector("[name='description']").value;
const givenTo = document.querySelector("[name ='givenTo']").value;
const createdByName = document.querySelector("[name = 'workerName']").value;
const task = {taskId, description, givenTo, createdByName, section: 'task-section'};
taskList.push(task);
window.localStorage.setItem("taskList", JSON.stringify(taskList));
// renderTaskList();
renderStoredList();
//Reload page after createNewTask to activate draggable
location.reload();
}
}
As I cannot add a comment yet, I'll post it here as an answer.
What I would do on my end to keep track of the taskId is to also store the latest taskId that was last used in my localStorage, that way, it would persist.
window.localStorage.setItem('lastTaskId', taskId);
And then simply take that each time the page loads.
Hope this helps!
What if you assign taskId based on previous length of the taskList:
const taskList = JSON.parse(window.localStorage.getItem("taskList")) || [];
let taskId = taskList.length