What does the combination of '#${parentId}' mean in JavaScript? - javascript

Here's the line:
const parent = document.querySelector(`#${parentId}`);
I know what the const parent, document.querySelector() and parentId mean and do.
I'm struggling to find out what the #, $ and {} do as a combination.
Here's the full .js:
(function(document) {
const buttons = document.querySelectorAll('[data-next]');
for (const item of buttons) {
const parentId = item.getAttribute('data-parent');
const parent = document.querySelector(`#${parentId}`);
console.log(parent);
const nextDivId = item.getAttribute('data-next');
const nextDiv = document.querySelector(`#${nextDivId}`);
if (!nextDiv) {
console.error('could not find next div for button ', item);
}
item.addEventListener('click', function() {
nextDiv.classList.toggle('hidden');
parent.classList.toggle('hidden');
});
}
})(document);

It is called Template Literals. From the documentation:
Template literals are string literals allowing embedded expressions. You can use multi-line strings and string interpolation features with them. They were called "template strings" in prior editions of the ES2015 specification.
Technically the code creates a string for document.querySelector() which will at the end of the day a selector for the parent's id.
Example code:
const parentId = 'randomid';
const result = `#${parentId}`;
console.log(result);
So you can think about that code as the following:
const parentId = item.getAttribute('data-parent');
const selector = `#${parentId}`;
const parent = document.querySelector(selector);
Read further here: Template Literals
I hope this helps!

${parentId} is a template literal expression
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals
Here it is saying, "get the variable parentId and prepend a hashtag, to indicate an id for the querySelector() function."
In the below code for example, the line renders as:
const parent = document.querySelector("#a")
const parentId = "a"
const parent = document.querySelector(`#${parentId}`)
parent.style.color = "#f00"
<div id="a">Text</div>

It's called a template literal.
You can think about it as putting some parsed code into a string.
So, if you want to put a variable into the string '#', you can do like this:
const someId = 'someid';
const idWithHashTag = `#${someId}`;
You have to use the backticks (), and then you can use${}` and do something inside the curly braces.

Related

Why does javascript reads var as a string if i add '+' to it?

I have field names in my firestore document as
videolink1-"some video link"
videolink2-"some video link"
videolink3-"some video link"
I am using a for loop to get all videolinks present in the document.
if (doc.exists) {
for (var i = 1; i == videocount; i++) { //videocount is 3
var data = doc.data();
var videolink = data.videolink+i;
//creating new paragraph
var p = '<p class ="trackvideostyle">'+"Your Video Link : "+String(videolink)+'</p>\';
document.getElementById("btn").insertAdjacentHTML('beforebegin', p);
}
But this for loop is creating var values which are being read as string and firestore is returning me NaN as I dont have these fields :
data.videolink+1
data.videolink+2 //Firestore is returning null as i dont have these document fields
data.videolink+3
How can I write for loop so that var values are created like this and firestore reads it as:
videolink1
videolink2
videolink3
I think you could try something like this,
var videolink = data[`videolink${i}`];
Refer: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals
you are using "data.videolink+i" and javascript does not evaluate the value after the "." instead it is treated as the object's property. you need to use [] for evaluation. try this I hope this will work
if (doc.exists) {
for (var i = 1; i == videocount; i++) {
var data = doc.data();
var videolink = data[videolink+i];
//creating new paragraph
var p = '<p class ="trackvideostyle">'+"Your Video Link :
"+String(videolink)+'</p>\';
document.getElementById("btn").insertAdjacentHTML('beforebegin',
p);
}
Why it doesn't work
The dot operator (property accessor) has higher precendense, so it is evaluated first, so you get the value of the property and then you concatenate the value of your i variable.
What should you do
You can use another property accessor - square brackets, just like in arrays:
data['videolink']
You can build your property name inside of the square brackets:
data['videolink' + i]
or using template literals:
data[`videolink${i}`]
You can do this by using
1. template strings
..
var videolink = `${data.videolink}${i}`
..
2. concat()
..
var videolink = data.videolink.concat(i.toString());
..

How to extract the content of the first paragraph in html string react native

I am working on a react native project and I have an html string json api response.
I am using react-native-render-html to render it, and I can get all paragraphs and apply specific things like number of lines ,etc.. . However I want to get only the first paragraph in the response.
str response='<p>text1</p> <p>text2</p> <p>text3</p>';
Is it possible to write a regular expression to get only the content of first paragraph which is for example text1 ?
I don't use React Native but in javascript you could do something like that:
const paragraphs = response.split("</p>")
const firstParagraph = paragraphs[0]+'</p>';
Or with a regex you can do something like that:
// extract all paragraphe from the string
const matches = [];
response.replace(/<p>(.*?)<\/p>/g, function () {
//use arguments[0] if you need to keep <p></p> html tags
matches.push(arguments[1]);
});
// get first paragraph
const firstParagraph = (matches.length) ? matches[0] : ""
Or like that (I think it is the best way in your case)
const response='<p>text1</p> <p>text2</p> <p>text3</p>';
const regex = /<p>(.*?)<\/p>/;
const corresp = regex.exec(response);
const firstParagraph = (corresp) ? corresp[0] : "" // <p>text1</p>
const firstParagraphWithoutHtml = (corresp) ? corresp[1] : "" // text1
Hope it will help
var response='<p>text1</p> <p>text2</p> <p>text3</p>';
var firstParagraphElement=response.split("</p>")[0] //firstparagraphElement="<p>text1"
var paragraphContent=firstParagraphElement.replace("<p>","") //paragraphContent="text1"
javascript split() function reference click
javascript replace() function reference click
In React Native you can also use parse5 to extract a string from HTML code. I have used this code in a project for doing so:
import parse5 from 'parse5'
const isText = (tagName): Boolean => tagName === '#text'
const processNode = (node): String => {
const nodeName = node.nodeName
if (isText(nodeName)) {
return node.value
}
if (!node.childNodes) {
return ''
}
return node.childNodes.map((child, index) => processNode(child)).join(' ')
}
export const htmlToText = (html): String => {
const root = parse5.parseFragment(html)
return processNode(root).replace(/\s+/g, ' ').trim()
}
Here is a simple JEST test for the function above:
test('when full document htmlToText should get text', () => {
const htmlToText1 = htmlToText("<html><head><title>titleTest</title></head><body><a href='test0'>test01</a><a href='test1'>test02</a><a href='test2'>test03</a></body></html>")
expect(htmlToText1)
.toBe(`titleTest test01 test02 test03`);
});

Using getElementsByTagName to find all hrefs in a variable

In a variable I'm holding HTML source code, which I obtained from DB. I'd like to search this content through for all the "a href" attributes and list them in a table.
Now I've found here how to search it in a DOM (like below), but how to use it to search within a variable?
var links = document.getElementsByTagName("a").getElementsByAttribute("href");
Got this currently, which is searching by RegEx, but it doesn't work very well:
matches_temp = result_content.match(/\b((?:[a-z][\w-]+:(?:\/{1,3}|[a-z0-9%])|www\d{0,3}[.]|[a-z0-9.\-]+[.][a-z]{2,4}\/)(?:[^\s()<>]+|\(([^\s()<>]+|(\([^\s()<>]+\)))*\))+(?:\(([^\s()<>]+|(\([^\s()<>]+\)))*\)|[^\s`!()\[\]{};:'".,<>?«»“”‘’&quote]))/ig);
In result_content I'm holding that HTML Source.
getElementsByTagName returns a nodelist that does not have a method called getElementsByAttribute but ONLY if you have DOM access
Without DOM (for example node.js)
const hrefRe = /href="(.*?)"/g;
const urlRe = /\b((?:[a-z][\w-]+:(?:\/{1,3}|[a-z0-9%])|www\d{0,3}[.]|[a-z0-9.\-]+[.][a-z]{2,4}\/)(?:[^\s()<>]+|\(([^\s()<>]+|(\([^\s()<>]+\)))*\))+(?:\(([^\s()<>]+|(\([^\s()<>]+\)))*\)|[^\s`!()\[\]{};:'".,<>?«»“”‘’&quote]))/ig;
const stringFromDB = `000
Something something 001 something`
stringFromDB.match(hrefRe).forEach(
(href) => console.log(href.match(urlRe)[0] )
);
// oldschool:
// stringFromDB.match(hrefRe).forEach(function(href) { console.log(href.match(urlRe)[0] ) });
In this code I create a DOM snippet first
Also I ONLY get anchors that have an href to begin with
NOTE the getAttribute so the browser does not try to interpret the URL
With the regex if you wanted to only match SPECIFIC types of href:
const re = /\b((?:[a-z][\w-]+:(?:\/{1,3}|[a-z0-9%])|www\d{0,3}[.]|[a-z0-9.\-]+[.][a-z]{2,4}\/)(?:[^\s()<>]+|\(([^\s()<>]+|(\([^\s()<>]+\)))*\))+(?:\(([^\s()<>]+|(\([^\s()<>]+\)))*\)|[^\s`!()\[\]{};:'".,<>?«»“”‘’&quote]))/ig;
const stringFromDB = `000
001`
let doc = document.createElement("div");
doc.innerHTML = stringFromDB
doc.querySelectorAll("a[href]").forEach(
(x) => console.log(x.getAttribute("href").match(re)[0])
);
Without the regex
const stringFromDB = `000
001`
let doc = document.createElement("div");
doc.innerHTML = stringFromDB
doc.querySelectorAll("a[href]").forEach(
(x) => console.log(x.getAttribute("href"))
);
Firstly, you shouldn't be using RegEx to parse HTML. This answer explains why.
Secondly, you're using getElementsByAttribute incorrectly - it does exactly what it says and gets elements by attributes. You should just use querySelectorAll on all elements with a href, and then map out the hrefs:
var hrefs = document.querySelectorAll("a[href*=http]");
var test = Array.prototype.slice.call(hrefs).map(e => e.href);
console.log(test);
Example
Example 1
Example 2
Example 3

JSON route matching via regex

Consider I have following JSON object
var urls = {
"GET/users/:id":1,
"POST/users":0
}
and if I have string "GET/users/10". How can I use this as key to get the value from urls JSON i.e. "GET/users/10" should match "GET/users/:id".
I don't want to iterate urls JSON and use regex for every key.
Is there a way to access JSON object using regex?
Thanks in advance.
Here is something that should work for you. I took some of the pieces from the Durandal router's RegEx matching logic which basically dynamically creates a regular expression object based on a defined route string and then tests with it against a passed string.
Here is the working example:
var urls = {
"GET/users/:id": 1,
"POST/users": 0
}
const getRouteRegExp = (
routeString,
routesAreCaseSensitive = false,
optionalParam = /\((.*?)\)/g,
namedParam = /(\(\?)?:\w+/g,
splatParam = /\*\w+/g,
escapeRegExp = /[\-{}\[\]+?.,\\\^$|#\s]/g
) => {
routeString = routeString.replace(escapeRegExp, '\\$&')
.replace(optionalParam, '(?:$1)?')
.replace(namedParam, function(match, optional) {
return optional ? match : '([^\/]+)';
})
.replace(splatParam, '(.*?)');
return new RegExp('^' + routeString + '$', routesAreCaseSensitive ? undefined : 'i');
}
const getRouteByString = (string) => {
var resultArr = Object.entries(urls).find(([k, v]) => {
var regEx = getRouteRegExp(k)
return regEx.test(string)
}) || []
return resultArr[0]
}
console.log(getRouteByString('GET/users/10'))
console.log(getRouteByString('POST/users'))
console.log(getRouteByString('POST/users2'))
So what you have is the getRouteRegExp function which is the main thing here which would compose a regular expression object based on a passed route.
After that we go and for each existing route defined in urls we create one RegExp and try to match it against the provided string route. This is what the find does. If one is found we return it.
Since we are doing Object.entries we return the 0 index which contains the result.
Since this comes straight from the Durandal bits it supports all the route expressions that are built in Durandal ... like:
Static route: tickets
Parameterized: tickets/:id
Optional Parameter: users(/:id)
Splat Route: settings*details
You can read more about Durandal Router here
From your question what I can understand is your key is dynamic, so you can do something like this:
var urls = {
"GET/users/:id":1,
"POST/users":0
}
let id = 10
const yourValue = urls["GET/users/" + id]
You can use this code to
var urls = {
"GET/users/:id":1,
"POST/users":0
}
var regex = /"([^"]+?)"\s*/g;
var urlsJson = JSON.stringify(urls);
let result = regex.exec(urlsJson)
if(result && result.length > 0) {
var keyJson = result[1];
var value = urls[keyJson]
console.log('value', value)
}
Try Something like this:
const urls = (id) => ({
[`GET/users/${id}`]:1,
"POST/users":0,
});
console.log(urls(2));
I hope it may be helpful.
The json would look fine, just do a replace on the url, so replace the ending integer with :id and then you have the key by which you can directly access the value in the json.
So:
var url = "GET/users/10";
var urls = {
"GET/users/:id":1,
"POST/users":0
}
url = url.replace(/users\/\d+/, 'users/:id');
console.log(urls[url]);
Do as many replaces on the url to convert all possible url's to the keys in your json.

Use loop and find html element's values JavaScript

I want to use vanilla js to loop through a string of html text and get its values. with jQuery I can do something like this
var str1="<div><h2>This is a heading1</h2><h2>This is a heading2</h2></div>";
$.each($(str1).find('h2'), function(index, value) {
/// console.log($(value).text());
});
using $(str) converts it to an html string as I understand it and we can then use .text() to get an element (h2)'s value.
but I want to do this within my node app on the backend rather than on the client side, because it'd be more efficient (?) and also it'd just be nice to not rely on jQuery.
Some context, I'm working on a blogging app. I want a table of contents created into an object server side.
This is another way using .innerHTML but uses the built-in iterable protocol
Here's the operations we'll need, the types they have, and a link to the documentation of that function
Create an HTML element from a text
String -> HTMLElement – provided by set Element#innerHTML
Get the text contents of an HTML element
HTMLElement -> String – provided by get Element#innerHTML
Find nodes matching a query selector
(HTMLElement, String) -> NodeList – provided by Element#querySelectorAll
Transform a list of nodes to a list of text
(NodeList, HTMLElement -> String) -> [String] – provided by Array.from
// html2elem :: String -> HTMLElement
const html2elem = html =>
{
const elem = document.createElement ('div')
elem.innerHTML = html
return elem.childNodes[0]
}
// findText :: (String, String) -> [String]
const findText = (html, selector) =>
Array.from (html2elem(html).querySelectorAll(selector), e => e.textContent)
// str :: String
const str =
"<div><h1>MAIN HEADING</h1><h2>This is a heading1</h2><h2>This is a heading2</h2></div>";
console.log (findText (str, 'h2'))
// [
// "This is a heading1",
// "This is a heading2"
// ]
// :: [String]
console.log (findText (str, 'h1'))
// [
// "MAIN HEADING"
// ]
// :: [String]
The best way to parse HTML is to use the DOM. But, if all you have is a string of HTML, according to this Stackoverflow member) you may create a "dummy" DOM element to which you'd add the string to be able to manipulate the DOM, as follows:
var el = document.createElement( 'html' );
el.innerHTML = "<html><head><title>aTitle</title></head>
<body><div><h2>This is a heading1</h2><h2>This is a heading2</h2></div>
</body</html>";
Now you have a couple of ways to access the data using the DOM, as follows:
var el = document.createElement( 'html' );
el.innerHTML = "<html><head><title>aTitle</title></head><body><div><h2>This is a heading1</h2><h2>This is a heading2</h2></div></body</html>";
// one way
el.g = el.getElementsByTagName;
var h2s = el.g("h2");
for(var i = 0, max = h2s.length; i < max; i++){
console.log(h2s[i].textContent);
if (i == max -1) console.log("\n");
}
// and another
var elementList = el.querySelectorAll("h2");
for (i = 0, max = elementList.length; i < max; i++) {
console.log(elementList[i].textContent);
}
You may also use a regular expression, as follows:
var str = '<div><h2>This is a heading1</h2><h2>This is a heading2</h2></div>';
var re = /<h2>([^<]*?)<\/h2>/g;
var match;
var m = [];
var i=0;
while ( match = re.exec(str) ) {
m.push(match.pop());
}
console.log(m);
The regex consists of an opening H2 tag followed by not a "<",followed by a closing H2 tag. The "*?" take into account zero or multiple instances of which there is at least zero or one instance.
Per Ryan of Stackoverflow:
exec with a global regular expression is meant to be used in a loop,
as it will still retrieve all matched subexpressions.
The critical part of the regex is the "g" flag as per MDN. It allows the exec() method to obtain multiple matches in a given string. In each loop iteration, match becomes an array containing one element. As each element is popped off and pushed onto m, the array m ultimately contains all the captured text values.

Categories

Resources