Create element and assign class in one step - javascript

Here are two ways that I know to create an element and assign it a class:
const el = document.createElement('div');
el.classList.add('foo');
const el = document.createElement('div');
foo.className = 'foo';
Is there a one-step solution for it? I tried
const el = document.createElement('div').classList.add('foo');
but it doesn't work.

Although the usual way to do it is a two-step technique, try this:
const el = Object.assign(document.createElement('div'), { className: 'foo' });
console.log(el);
console.log(el.className);

You're stuck with either using an HTML string and innerHTML or trying jQuery which allows the chaining of commands, but for something so small it doesn't make sense to bring jQuery into the mix, what's the reason you needed a one-liner?
For a rough working example in vanilla js
var myHTML = "<div class='hello'></div>";
document.body.innerHTML += myHTML

Related

js: how to simplify html string

is there any way to simplify the HTML string? Like removing all redundant tags from the string.
For instance:
Source HTML:
<div><span><span>1</span></span><span>2</span></div>
Expected output:
<div><span>12</span></div>
(or even less)
<div>12</div>
I've known some libs like quilljs can do this, but it's a huge library, kind of overkill for my case.
also, https://github.com/htacg/tidy-html5 is kind of what I want, but it does not have a js release
You can try using the DOMParser:
let s = `<div><span><span>1</span></span><span>2</span></div>`
let d = new DOMParser()
let doc = d.parseFromString(s, 'application/xml')
let tag = doc.children[0].tagName
let text = doc.children[0].textContent
let result = `<${tag}>${text}</${tag}>`
console.log(result)
Please refer to the below code, It may help you to go further.
var childs = document.querySelectorAll("div#parent")
var tmpTexts = []
for (const c of childs) {
if (tmpTexts.includes(c.innerText)) continue
tmpTexts.push((c.innerText).trim())
c.parentNode.removeChild(c)
}
tmpTextArr = tmpTexts[0].split('\n');
console.log(tmpTextArr);
const para = document.createElement("div");
tmpTextArr.forEach(function(text) {
var node = document.createElement("div");
var nodeTxt = document.createTextNode(text);
node.appendChild(nodeTxt);
para.appendChild(node)
});
document.body.appendChild(para);
https://jsfiddle.net/Frangly/pnLgr8ym/66/
In tmpTexts, for every new line - you should add a div tag.
Create a new Element and iterate the tmpTexts array and a div tag by using innerHTML

Most efficient way to create a div with several children

I'm trying to create a function which takes an object with a few parameters and returns a newly created div.
From what i can see, there seem to be two main ways to accomplish this:
creating each element by itself and appending it
creating a template literal and set the divs innerHTML
the inputs of the functions are not user generated, so i don't think using template literals will create a security issue (please educate me if i'm wrong)
So now my questions are the following:
is one more efficient than the other?
is one preferred?
are there any other concerns?
is there an even more efficient/better way?
below you can see the two solutions i've come up with.
function createDiv (entry) {
const div = document.createElement('div')
div.classList.add('exchange')
div.id = entry.exchange
const img = document.createElement('img')
img.src = `/static/img/${entry.img}.png`
img.alt = entry.name
img.classList.add('logo-image')
div.appendChild(img)
const link = document.createElement('a')
link.href = entry.url
link.classList.add('name')
link.innerText = entry.name
div.appendChild(link)
const routing = document.createElement('span')
routing.innerText = entry.routing ? entry.routing : ''
div.appendChild(routing)
const price = document.createElement('span')
price.innerText = entry.price
price.classList.add('price')
div.appendChild(price)
return div
}
function createDiv (entry) {
const div = document.createElement('div')
div.classList.add('exchange')
div.id = entry.exchange
let text = `
<img class="logo-image" src="/static/img/${entry.img}.png" alt="${entry.name}">
<a class="exchange-name" href="${entry.url}">${entry.name}</a>
<span>${routing.innerText = entry.routing ? entry.routing : ''}</span>
<span class="price">${entry.price}</span>
`
div.innerHTML = text
return div
}
Thank you in advance!
What about doing something like the following?
const createDiv = ({ exchange, img, name, url, routing: entryRouting, price }) => {
return `
<div class="exchange" id="${exchange}">
<img class="logo-image" src="/static/img/${img}.png" alt="${name}">
<a class="exchange-name" href="${url}">${name}</a>
<span>${routing.innerText = entryRouting || ''}</span>
<span class="price">${price}</span>
</div>
`;
}
In this case you are getting the full power of the template literals and of the object destructing.
About the values, you should validate them in some way before storing in the database and sanitize the HTML before getting it back. Some sort of easy validation with regex could be enough for validation. For sanitizing you can choose one of the many libraries like the this https://www.npmjs.com/package/sanitize-html.
About performances, I wouldn't take it too seriously until you do many iterations. As far as I see it is a onetime function call. So I would go for the cleaner way: template strings. But if you are curious, the template string is the fastest. The first approach is almost 100% slower. You can check the results of the test I did over 100 iterations here https://jsbench.me/7gkw1t31rs/2.
Remember that the approach I am telling you will need an innerHTML once the createDiv function returns its value.

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

$(selector, element) Native JS alternative

Hi I'm trying to remove all jQuery from my platform one line at a time.
But I'm having some trouble finding a replacement for this
$('[data-attribute="value"]', GenericHTMLElement);
I was hoping it would be something simple like
var div = document.createElement('div');
div.innerHTML = '<div><span data-attribute="value"></span><span data-something-else="1000"></span></div>';
var b = div.childNodes;
var a = b.querySelector('[data-attribute="value"]');
But that's not working either. Does have any suggestions for me?
As commented,
childNodes will give you a list of elements. This list will not have querySelector. If you loop over nodes, you should be able to get it though. But, my suggestion is just do div.querySelector(...)
To be specific, it will be of type NodeList. This is a collection of nodes. So you cannot run querySelector on it. You can either loop over all nodes and do querySelector on them or just so this operation on parent div.
var div = document.createElement('div');
div.innerHTML = '<div><span data-attribute="value">Dummy Text</span><span data-something-else="1000"></span></div>';
var b = div.childNodes;
console.log('Type of childNodes is: ', Object.prototype.toString.call(b))
// getting element using loop over childNodes
for(var i = 0; i<b.length; i++) {
var el = b[i].querySelector('[data-attribute="value"]');
el && console.log(el.textContent)
}
// getting element using parent elenent.
var el1 = div.querySelector('[data-attribute="value"]');
console.log(el1.textContent)
First you need to understand what the first code does. It searches for given selector, limiting it to HTMLElementObject scope. Understanding that we can try to do something similar.
From MSDN example, he is using body element:
var el = document.body.querySelector("style[type='text/css'], style:not([type])");
They have this example with data-attributes, take a look please.
The reason your attempt isn't working is that you're trying to call querySelector on a NodeList, which doesn't have a querySelector method.
If you try it on a single element, it works fine:
function mySelect(selector, el) {
return el.querySelector(selector);
}
var div = document.createElement('div');
div.innerHTML = '<div><span data-attribute="value"></span><span data-something-else="1000"></span></div>';
var b = div.childNodes[0];
console.log(mySelect('[data-attribute="value"]', b));
But this makes it so that mySelect(selector, el) is nothing more than an alias for el.querySelector(selector).
Presumably, you'd want to be able to evaluate a selector on multiple elements at once, and return multiple results, like jQuery does. In that case, you can do so by making some adjustments:
function flatMap(values, f) {
return Array.prototype.concat.apply([], values.map(f));
}
function mySelect(selector, els) {
return flatMap(els.length ? Array.from(els) : [els], function (el) {
return Array.from(el.querySelectorAll(selector));
});
}
var div = document.createElement('div');
div.innerHTML = '<div><span data-attribute="value">span 1</span><span data-something-else="1000"></span></div><div><span data-attribute="value">span 2</span></div>';
console.log(mySelect('[data-attribute="value"]', div.childNodes));
console.log(mySelect('[data-attribute="value"]', div.childNodes[0]));

What's the effectiviest way of creating an element with multiple attributes and appending it to another element and have it available as a variable??

I wonder what's the effectiviest way of creating an element with id='id' class='class' attr='attr' .. and appending it to another element and have it available as a variable?
So far I can do it with one line of code. But it requires a new set of javascript framework. I just want to make sure that there is no effectivier way first.
My solution is to create a javascript framework, which got the following syntax:
var variable = elementTagAppend/Prepend/After/Before('JQuerySelector', elementToAppend/Prepend/After/BeforeTo);
For instance, if I want to create an div element with id='id', class='class', attr='attr' and append it to an another element called "another"
var variable = divAppend('#id.class[attr = "attr"]', another); Very effective right?
Or if I want to prepend a form element with id='inputForm', class='inputForms':
var variable = formPrepend('#inputForm.inputForms', another);
var element = $("<div/>", {
id: "id",
class: "class",
attr: "attr"
}).appendTo("#element");
appendTo returns a JQuery object, you can read more on http://api.jquery.com/appendTo/
Edit: It looks like you're asking if an effective way to create elements is having methods for all HTML tags, so that your library doesn't have to do any sort of text parsing/regex.
This isn't a solution as it makes development a lot slower.
One way is to use a function and DOM methods:
<script type="text/javascript">
var elementData = {
tagName: 'div',
properties: {
id: 'div0',
onclick: function(){alert(this.id);},
className: 'someClass'
},
content: '<h1>New Div</h1>'
}
function elementBuilder(obj) {
var el = document.createElement(obj.tagName);
var props = obj.properties;
for (var p in props) {
if (props.hasOwnProperty(p)) {
el[p] = props[p];
}
}
if (typeof obj.content == 'string') {
el.innerHTML = obj.content;
}
return el;
}
</script>
<button onclick="
var newElement = elementBuilder(elementData);
document.body.insertBefore(newElement, this.nextSibling);
">Insert element</button>
The above could be expanded to create more than one element per call, I'll leave that you.
Another method is to take an HTML string, convert it to an element (or elements) and return it in a document fragment:
<script type="text/javascript">
var htmlString = '<p><b>paragraph</b></p>' +
'<p>Another p</p>';
function byInnerHTML(s) {
var d = document.createElement('div');
d.innerHTML = s;
var frag = document.createDocumentFragment();
while (d.firstChild) {
frag.appendChild(d.firstChild);
}
return frag;
}
</script>
<button onclick="
var newContent = byInnerHTML(htmlString);
document.body.insertBefore(newContent, this.nextSibling);
">Insert element</button>
The second one returns a document fragment that can be inserted into the page. Of course if you want to generate parts of a table, the second method isn't quite so good.
In tune with a pure JavaScript implementation, here's my take on the problem:
function createElement(type, attributes, parent){
var node = document.createElement(type);
for (i in attributes){
node.setAttribute(i, attributes[i])
}
if (parent && parent.__proto__.appendChild){
parent.appendChild(node)
}
return node;
}
And you use it like this:
createElement("span", { "id": "theID", "class": "theClass", "title": "theAttribute"}, document.getElementById("theParent"))
I kept one of your initial requirements: append the node to some other element and also return it.
I particularly like RobG's second example with the fragment. Definitely something I'd try if I were using plain strings to represent HTML.
If it doesn't warrant the use-case, it doesn't always make sense to import a JS framework. jQuery is no exception. Should you decide more complex workflows are needed, that's your warrant! ;)

Categories

Resources