I need to load some data and insert it in two ways into an li. First, it is inserted as a <p>. Second,it is inserted as a hidden input with the value specified. How can I do that? I mean, if I do it with the innerHTML property it works, but I need to add 2 elements, no only one. And when I try to use the appendChild it gives the error:
infoPersonal.js:22 Uncaught (in promise) TypeError: Failed to execute
'appendChild' on 'Node': parameter 1 is not of type 'Node'.
what can I do?
EDIT: in the code below it only enters if the condition is met, but it is supossed to add the input with every el
const datos= () => {
const token = localStorage.getItem('token')
if (token) {
return fetch('https://janfa.gharsnull.now.sh/api/auth/me', {
method: 'GET',
headers: {
'Content-Type': 'application/json',
authorization : token,
},
})
.then( x => x.json())
.then( user =>{
const datalist = document.querySelectorAll(".data")
datalist.forEach( function(el){
var input
const template = '<p>' + user[el.getAttribute("data-target")] + '</p>'
if (el.getAttribute("data-target")==="birth") {
input = `<input class="input-date" type ="date" value="${user[date]}" hidden>`
}
el.innerHTML=template //this works
el.appendChild(input) //this doesn't
})
})
}
}
window.onload=() =>{
datos()
}
appendChild expects a Node or element. You have two options:
Create the element:
input=document.createElement("input");
input.type="date";
input.className="input-date";
input.value=user.date;
input.hidden=true;
Or use innerHTML. Of course it will replace the contents of el, but you could use a placeholder or dummy element.
var div=document.createElement("div");
div.innerHTML="<input ...";
input=div.children[0];
I'd do the first thing. Or use a framework if you want to write less, but it's a little overkill just for this.
You can use the insertAdjacentHTML() method on an element. First parameter takes a string denoting the position, and second argument is the HTML string. Really good browser support at this point too.
Element.insertAdjacentHTML('beforeend', '<p class="someClass">Hello, World</p>');
Related
I want to create a DOM element from an HTML string. Inside that string, I have a button that should call a global function called downloadFile with predefined arguments.
My first try:
<button onclick="downloadFile(${message_result})">Download</button>
But this failed to work.
Whole code looks like this
function downloadFile(result){
console.log(result)
}
(() => {
const msg = {user: 'User', message: 'Message', result: {}} // any
var markup = `
<h4>${msg.user}</h4>
<p>${msg.message}</p>
<button onclick="downloadFile(message_result)">Download</button>
`
document.body.innerHTML = markup
})()
the error:
Uncaught ReferenceError: message_result is not defined
at HTMLButtonElement.onclick
any suggestions how i can pass a variable into my function
Let's first solve your problem then talk about a better approach.
If we assume msg.result is a string: You need to wrap it with quote marks.
<button onclick="downloadFile('${message_result}')">Download</button>`;
But if your msg.result is not a simple string or you want to take the right path to the solution, then we need to move next approach.
// This is your downloadFile function
const downloadFile = (data) => {
console.log(data)
}
// This function creates download buttons
const createDownloadButton = (response) => {
const markup = `
<h4>${response.user}</h4>
<p>${response.message}</p>
<button>Download</button>
`
const container = document.createElement("div")
container.innerHTML = markup;
// Here, we assign "click" functionality dynamically even before we inject the element into DOM
container.getElementsByTagName("button")[0].addEventListener('click', () => {
downloadFile(response.result)
})
document.body.append(container)
}
// Fetch your response from an external service
const msg = {
user: 'Username',
message: 'Message',
result: {
file: {
link: 'https://stackoverflow.com'
}
}
}
// Call function to create download button
createDownloadButton(msg)
If message_result is a variable, like msg.message, then you need to reference it similarly.
var markup=`
<h4>${msg.user}</h4>
<p>${msg.message}</p>
<button onclick="downloadFile('${message_result}')">Download</button>`;
I am currently working with Cypress and have created a function that randomly clicks an element from a list. I am trying to also get the text from that same function for future assertions. The problem is I am unable to return the text properly. The code I currently have is:
export function selectRandomFromList(listLocator, elementLocator) {
cy.get(listLocator).within(() => {
let numberOfElements = Cypress.$(listLocator + ' ' + elementLocator).length
let selected = Cypress._.random(0, numberOfElements - 1)
cy.get(elementLocator).eq(selected).then(($text) => {
const text = $text.text()
return text
}).click()
})
}
I was hoping that I could then in my test run this function, do the click and then store the returned text in a variable for future checking. What am I doing wrong? Also tried some other stuff with promises and such, when the code was saying I am trying to mix sync and async..
Forgot to add. This is in the support file and I want to use the text variable in the test file. Something like this:
var text = function.selectRandomFromList('[class*=***]', 'li ul li button')
After which I should have the text of the clicked button in text.
Cypress commands work the same way as Promises. You need to return a Promise when inside a then() callback. See doc here.
You may use cy.wrap($text).invoke('text') inside your then() callback instead of returning a string. But then, your click() would not work because your then() would yield a text value that cannot be clicked.
I suggest not using the within() and working directly with the elements. You'll end up with the same result, but with less complexity. I use the within() command when I need to perform several actions on several elements within a div container for example.
You can perform what you need with default commands instead of a function:
let mixLocator = listLocator + ' ' + elementLocator;
cy.get(mixLocator).its('length').then(elementCount => {
let selected = Cypress._.random(elementCount - 1); // lower = 0 is default
cy.get(mixLocator).eq(selected).click().invoke('text').as('selectedText'); // saving the text as an alias to be used later
});
Here, the invoke('text') should still work even if it is after the click(), but it is impossible to do the opposite invoke('text').click() because the invoke('text') command yields a string. If it doesn't, call it once to get the text and again to click it:
cy.get(mixLocator).eq(selected).invoke('text').as('selectedText');
cy.get(mixLocator).eq(selected).click();
or:
cy.get(mixLocator).eq(selected).then(selectedElement => {
cy.wrap(selectedElement).invoke('text').as('selectedText');
cy.wrap(selectedElement).click();
});
You may then use your saved alias later:
cy.get('#selectedText').then(selectedText => {
// use selectedText here
});
I often prefer to chose random elements in our tests to have a better coverage. I came up with a custom commands:
commands.js
Cypress.Commands.add('any', { prevSubject: 'element' }, (subject, size = 1) => {
cy.wrap(subject).then(elementList => {
elementList = (elementList.jquery) ? elementList.get() : elementList;
elementList = Cypress._.sampleSize(elementList, size);
elementList = (elementList.length > 1) ? elementList : elementList[0];
cy.wrap(elementList);
});
});
I use it like this:
cy.get(elementLocator).any().click();
or
cy.get(elementLocator).any(5).each(randomElement => {
cy.wrap(randomElement).click();
});
I am parsing a webpage and trying to get the text values from it.
I am using cheerio to be able to do this with node.js.
Currently whenever I parse a tag it returns {{status}} this is because the value is an environment variable, but I want to be able to read the actual value (in this case it is "2").
This is what I have currently got:
const rp = require('request-promise');
const url = 'my url';
const $ = require('cheerio');
rp(url)
.then(function(html){
//success!
console.log($('.class-name div', html).text());
})
.catch(function(err){
//handle error
});
I have also tried using .html(), .contents() but still not success.
Do I have to change the second parameter in $('.class-name DIV', <PARAMETER>) to achieve what I am after?
You don't provide any URL or HTML you're trying to parse.
So, with cheerio, you can use selector like this format.
$( selector, [context], [root] )
Means search the selector inside context, within root element (usually HTML doc string), selector and context can be a string expression, DOM Element, array of DOM elements, or cheerio object.
Meanwhile:
$(selector).text() => Return innerText of the selector
$(selector).html() => Return innerHTML of the selector
$(selector).attr('class') => Return value of class attribute
But cheerio parser is difficult to debug.
I've used cheerio for a while and sometimes this can be a headache.
So i've found jsonframe-cheerio, a package that parse that HTML tags for you.
In this working example below, as you can see it parse cheerio perfectly.
It will translate a format called frame to extract innerText, attributes value, even filter or match some regular expression.
HTML source (simplified for readibility)
https://www.example.com
<!doctype html>
<html>
<head>
<title>Example Domain</title>
<meta charset="utf-8" />
</head>
<body>
<div>
<h1>Example Domain</h1>
<p>This domain is for use in illustrative examples in documents. You may use this
domain in literature without prior coordination or asking for permission.</p>
<p>More information...</p>
</div>
</body>
</html>
CHeerIO
const request = require ('request-promise')
const cheerio = require ('cheerio')
const jsonfrm = require ('jsonframe-cheerio')
const url = 'https://www.example.com'
let $ = null
;(async () => {
try {
const response = await request(url)
$ = cheerio.load (response)
jsonfrm($)
let frame = {
articles : { // This format will loop every matching selectors occurs
_s : "body > div", // The root of every repeating item
_d : [{
"titling" : "h1", // The innerText of article h1
"excerpt" : "p", // The innerText of article content p
"linkhref": "a[href] # href" // The value of href attribute within a link
}]
}
}
const displayResult = $('body').scrape(frame, { string: true } )
console.log ( displayResult )
} catch ( error ) {
console.log ('ERROR: ', error)
}
})()
I have the following code that I am running on a website:
let details = await page.$$eval(
"#od-subtotals .a-row:not(.a-spacing-mini)",
nodes =>
nodes.map(async n => {
let heading = await n.$eval(".a-text-left span", nn => nn.innerText);
let amount = await n.$eval(".a-text-right span", nn => nn.innerText);
return { heading: heading, amount: amount };
})
);
The $$eval method works fine and if I were to run the map simply on the $$eval(sel, nodes => nodes.map(n => n.innerText), I receive an array.
Now, I am trying to separate the node even further. When I read the docs it says the following:
page.$$eval(selector, pageFunction[, ...args])
selector <string> A selector to query page for
pageFunction <function(Array<Element>)> Function to be evaluated in browser context
So, my thinking was that I loop through elements on the page on which I run the .$eval method which has the following characteristics:
elementHandle.$eval(selector, pageFunction[, ...args])
selector <string> A selector to query page for
pageFunction <function(Element)> Function to be evaluated in browser context
I do receive the aforementioned error:
Uncaught (in promise) TypeError: n.$eval is not a function
at __puppeteer_evaluation_script__:7
at Array.map (<anonymous>)
at VM163 __puppeteer_evaluation_script__:2
(anonymous) # __puppeteer_evaluation_script__:7
(anonymous) # __puppeteer_evaluation_script__:2
So, it seems like the Element returned from .$$eval is not actually an ElementHandle and as such, cannot be used with the $eval function. I was looking through the docs and there does not seem to be a way to get them converted either.
The code would need to be changed to:
let details = await page.$$eval(
"#od-subtotals .a-row:not(.a-spacing-mini)",
nodes =>
nodes.map(async n => {
let heading = n.querySelector(".a-text-left span").innerText;
let amount = n.querySelector(".a-text-right span").innerText;
return { heading: heading, amount: amount };
})
);
Presumably, there is also a way where we could use .$$ and then iterate over the Array of ElementHandle and then using $eval.
I want to code an html button with jQuery, when chart button is pressed, will take values from some text inputs, prepare a json of them and will perform a get request including the json with values.
$(".chart").click(function(){
var args = {}
args['fx'] = $("#tolerancia").val()
args['a'] = $("#a").val()
args['b'] = $("#b").val()
args['tolerancia'] = $("#tolerancia").val()
args['iteracionesMaximas'] = $("#iteracionesMaximas").val()
$.get("/taylor",{args},function(resultado){
console.log(resultado)
})
})
I'm getting an Invalid object initializer message in console right on line of:
$.get("/taylor",{args},function(resultado){
What have I got wrong?
Change your code to:
$.get("/taylor", args, function(resultado) {
console.log(resultado)
});
Note: {args} is invalid syntax. args is already an object, so you don't need to wrap it in any other brackets.