html get's rendered inside code element instead of a string - javascript

I want to auto-generate a html-preview for my components, but my html gets rendered as a node instead of displaying a string...
this is my simplified example
const btns = document.querySelectorAll('.preview')
const previewContainer = document.getElementById('previewContainer')
const pre = document.createElement('pre')
previewContainer.appendChild(pre)
btns.forEach((btn)=> {
const code = document.createElement('code')
pre.appendChild(code)
code.innerHTML = btn.outerHTML
console.log(typeof btn.outerHTML)
})
<button class="preview">label1</button>
<button class="preview">label2</button>
<div id="previewContainer"></div>
Have also created a codepen

Use textContent instead of innerHTML
I do not use innerText because
Don't get confused by the differences between Node.textContent and HTMLElement.innerText. Although the names seem similar, there are important differences:
textContent gets the content of all elements, including <script> and <style> elements. In contrast, innerText only shows “human-readable” elements.
textContent returns every element in the node. In contrast, innerText is aware of styling and won’t return the text of “hidden” elements.
Moreover, since innerText takes CSS styles into account, reading the value of innerText triggers a reflow to ensure up-to-date computed styles. (Reflows can be computationally expensive, and thus should be avoided when possible.)
const btns = document.querySelectorAll('.preview')
const previewContainer = document.getElementById('previewContainer')
const pre = document.createElement('pre')
previewContainer.appendChild(pre)
btns.forEach((btn)=> {
const code = document.createElement('code')
pre.appendChild(code)
code.textContent = btn.outerHTML; // show the code as text
console.log(typeof btn.outerHTML)
})
<button class="preview">label1</button>
<button class="preview">label2</button>
<div id="previewContainer"></div>

Use innerText instead of innerHTML
const btns = document.querySelectorAll('.preview')
const previewContainer = document.getElementById('previewContainer')
const pre = document.createElement('pre')
previewContainer.appendChild(pre)
btns.forEach((btn)=> {
const code = document.createElement('code')
pre.appendChild(code)
code.innerText = btn.outerHTML
console.log(typeof btn.outerHTML)
})
<button class="preview">label1</button>
<button class="preview">label2</button>
<div id="previewContainer"></div>
.innerHTML parses the string as HTML. .innerText keeps it as a string and appends a string rather than HTML

Related

Wrapping a string in a div in pure javascript

Is there a way in pure javascript to wrap a nacked string?
I have a string that I'm splitting based on a character to separate the header from the rest of the content. I would very much like to style that header, but i can't seem to find a good way to wrap a div around it with a class.
All I can seem to find is wrapping a div around something that already has other elements.
My code looks like this
var string = "Title*This is the very long content";
var title = string.split('*')[0]
var body = string.split('*')[1]
//put them back together
string = title + body;
but i can't seem to find a good way to wrap a div around it with a
class?
You can create an element (which is at the end a tag HTML) with createElement
and attach it a class with className
let string = "Title*This is the very long content";
/*
let title = string.split('*')[0]
let body = string.split('*')[1]
*/
let [title, body] = string.split('*'); // Destructuring assignment
let headerTitle = document.createElement('h1');
headerTitle.textContent = title;
headerTitle.className = "red";//headerTitle.classList.add('red');
let bodyHTML = document.createElement('p');
bodyHTML.textContent = body;
document.querySelector('#content').innerHTML = headerTitle.innerHTML +"<br/>"+ bodyHTML.innerHTML;
.red{
color: red;
}
<div id="content" />
Tip Try to avoid var keyword for declaring variable and replace them with either let or const and better using Destructuring assignment

How to make mutliple times of insertAdjacentElement

How do I make multiple times of insertAdjacentElement like below?
test.insertAdjacentElement('afterend',elm1);
test.insertAdjacentElement('afterend',elm2);
test.insertAdjacentElement('afterend',elm3);
I can make a function to refactor it, but is there any shortcut to doing this? Like:
test.insertAdjacentElement('afterend',elm1, elm2, elm3);
If you're inserting at the end of a container, you can use append, which accepts multiple arguments.
containerOfTest.append(elm1, elm2, elm3)
Otherwise, a very simple loop would do.
for (const elm of [elm1, elm2, elm3]) {
test.insertAdjacentElement('afterend',elm);
}
Another option is to create a DocumentFragment, insert all elements into it, and then insert the fragment (only once) into the DOM.
const test = document.querySelector('.test');
const elm1 = document.createElement('div');
const elm2 = document.createElement('div');
const elm3 = document.createElement('div');
const fragment = new DocumentFragment();
fragment.append(elm1, elm2, elm3);
test.parentElement.insertBefore(fragment, test.nextElementSibling);
console.log(document.body.innerHTML);
<div class="test">test</div>

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

Problems when parsing nested html tags from string

I have this code that's to parse a string into html and display the text of each element.
That's working good except when I have nested tags for example <div><p>Element 1</p><p>Element 2</p></div>. In this case, the code displays <p>Element 1</p><p>Element 2</p>.
How can I do to get each tags one after the other ? (Here I want Element 1 and then Element 2)
Here's the code :
let text = new DOMParser().parseFromString(stringHtml, 'text/html');
let textBody = text.body.firstChild;
while (textBody) {
alert(textBody.innerHTML);
// other actions on the textBody element
textBody = textBody.nextSibling;
}
Thanks for helping me out
It sounds like you want a recursive function that prints the textContent of itself, or of its children, if it has children:
const stringHtml = '<div><p>Element 1</p><p>Element 2</p></div><div><p>Element 3</p><p>Element 4</p></div>';
const doc = new DOMParser().parseFromString(stringHtml, 'text/html');
const showElms = parent => {
const { children } = parent;
if (children.length) Array.prototype.forEach.call(children, showElms);
else console.log(parent.textContent);
}
showElms(doc.body);
That's assuming you want to iterate over the actual elements. If you want all text nodes instead, then recursively iterate over the childNodes instead.

How to correctly use innerHTML to create an element (with possible children) from a html string?

Note: I do NOT want to use any framework.
The goal is just to create a function that will return an element based on an HTML string.
Assume a simple HTML Document like such:
<html>
<head></head>
<body>
</body>
</html>
All functions mentioned are in included the head section and all DOM creation/manipulation is done at the end of the body in a script tag.
I have a function createElement that takes a well formed HTML String as an argument. It goes like this:
function createElement(str)
{
var div = document.createElement('div');
div.innerHTML = str;
return div.childNodes;
}
Now this functions works great when you call it like such:
var e = createElement('<p id="myId" class="myClass">myInnerHTML</p>');
With the minor (possibly HUGE) problem that the element created isn't a 'true' element, it still has a parentNode of 'div'. If anyone knows how to fix that, then that would be awesome.
Now if I call the same function with a more complex string:
var e = createElement('<p id="myId" class="myClass">innerHTML<h2 id="h2ID" class="h2CLASS">Heading2</h2></p>');
It creates TWO children instead of ONE child with another child having another child.Once you do div.innerHTML = str. The innerHTML instead of
`<p id="myId" class="myClass">innerHTML <h2 id="h2ID" class="h2CLASS">Heading2</h2> </p>`
turns to
`<p id="myId" class="myClass">innerHTML</p> <h2 id="h2ID" class="h2CLASS">Heading2</h2>`
Questions:
Can I somehow get an element without a parent node after using .innerHTML?
Can I (in the case of the slightly complex string) get my function to return ONE element with the appropriate child instead of two elements. [It actually returns three, <p.myClass#myId>,<h2.h2CLASS#h2ID>, and another <p>]
This is similar to the answer from palswim, except that it doesn't bother with creating a clone, and uses a while() loop instead, always appending the node at [0].
function createElement( str ) {
var frag = document.createDocumentFragment();
var elem = document.createElement('div');
elem.innerHTML = str;
while (elem.childNodes[0]) {
frag.appendChild(elem.childNodes[0]);
}
return frag;
}
You'd have to attach the new element somewhere. Try using a DocumentFragment object in conjunction with the div you created:
function createElement(str) {
var div = document.createElement('div');
div.innerHTML = str;
var container = document.createDocumentFragment();
for (var i=0; i < div.childNodes.length; i++) {
var node = div.childNodes[i].cloneNode(true);
container.appendChild(node);
}
return container.childNodes;
}
It's more overhead, but it does what you want. Note that DOM elements' .insertAdjacentHTML member function is coming in HTML5.
For that complex string you passed, it isn't valid XHTML syntax - you can't have a block element as a child of <p> (<h2> is a block level element).

Categories

Resources