How to insert a large block of HTML in JavaScript? - javascript

If I have a block of HTML with many tags, how do insert it in JavaScript?
var div = document.createElement('div');
div.setAttribute('class', 'post block bc2');
div.innerHTML = 'HERE TOO MUCH HTML that is much more than one line of code';
document.getElementById('posts').appendChild(div);
How do I do it right?

Template literals may solve your issue as it will allow writing multi-line strings and string interpolation features. You can use variables or expression inside string (as given below). It's easy to insert bulk html in a reader friendly way.
I have modified the example given in question and please see it below. I am not sure how much browser compatible Template literals are. Please read about Template literals here.
var a = 1, b = 2;
var div = document.createElement('div');
div.setAttribute('class', 'post block bc2');
div.innerHTML = `
<div class="parent">
<div class="child">${a}</div>
<div class="child">+</div>
<div class="child">${b}</div>
<div class="child">=</div>
<div class="child">${a + b}</div>
</div>
`;
document.getElementById('posts').appendChild(div);
.parent {
background-color: blue;
display: flex;
justify-content: center;
}
.post div {
color: white;
font-size: 2.5em;
padding: 20px;
}
<div id="posts"></div>

This answer does not use backticks/template literals/template strings (``), which are not supported by Internet Explorer.
Using HTML + JavaScript:
You could keep the HTML block in an invisible container (like a <script>) within your HTML code, then use its innerHTML at runtime in JS
For example:
// Create a temporary <div> to load into
var div = document.createElement('div');
div.setAttribute('class', 'someClass');
div.innerHTML = document.getElementById('blockOfStuff').innerHTML;
// You could optionally even do a little bit of string templating
div.innerHTML = div.innerHTML
.replace(/{VENDOR}/g, 'ACME Inc.')
.replace(/{PRODUCT}/g, 'Best TNT')
.replace(/{PRICE}/g, '$1.49');
// Write the <div> to the HTML container
document.getElementById('targetElement').appendChild(div);
.red {
color: red
}
<script id="blockOfStuff" type="text/html">
Here's some random text.
<h1>Including HTML markup</h1>
And quotes too, or as one man said, "These are quotes, but
'these' are quotes too."<br><br>
<b>Vendor:</b> {VENDOR}<br>
<b>Product:</b> {PRODUCT}<br>
<b>Price:</b> {PRICE}
</script>
<div id="targetElement" class="red"></div>
Idea from this answer: JavaScript HERE-doc or other large-quoting mechanism?
Using PHP:
If you want to insert a particularly long block of HTML in PHP you can use the Nowdoc syntax, like so:
<?php
$some_var = " - <b>isn't that awesome!</b>";
echo
<<<EOT
Here's some random text.
<h1>Including HTML markup</h1>
And quotes too, or as one man said, "These are quotes, but 'these' are quotes too."
<br><br>
The beauty of Nowdoc in PHP is that you can use variables too $some_var
<br><br>
Or even a value contained within an array - be it an array from a variable you've set
yourself, or one of PHP's built-in arrays. E.g. a user's IP: {$_SERVER['REMOTE_ADDR']}
EOT;
?>
Here's a PHP Fiddle demo of the above code that you can run in your browser.
One important thing to note: The <<<EOT and EOT; MUST be on their own line, without any whitespace before them!
Why use Nowdoc in PHP?
One huge advantage of using Nowdoc syntax over the usual starting and stopping your PHP tag is its support for variables. Consider the normal way of doing it - shown in the example below:
<?php
// Load of PHP code here
?>
Now here's some HTML...<br><br>
Let's pretend that this HTML block is actually a couple of hundred lines long, and we
need to insert loads of variables<br><br>
Hi <?php echo $first_name; ?>!<br><br>
I can see it's your birthday on <?php echo $birthday; ?>, what are you hoping to get?
<?php
// Another big block of PHP here
?>
And some more HTML!
</body>
</html>
Contrast that to the simplicity of Nowdoc.

Despite the imprecise nature of the question, here's my interpretive answer.
var html = [
'<div> A line</div>',
'<div> Add more lines</div>',
'<div> To the array as you need.</div>'
].join('');
var div = document.createElement('div');
div.setAttribute('class', 'post block bc2');
div.innerHTML = html;
document.getElementById('posts').appendChild(div);

If I understand correctly, you're looking for a multi-line representation, for readability? You want something like a here-string in other languages. Javascript can come close with this:
var x =
"<div> \
<span> \
<p> \
some text \
</p> \
</div>";

The easiest way to insert html blocks is to use template strings (backticks). It will also allow you to insert dynamic content via ${...}:
document.getElementById("log-menu").innerHTML = `
<a href="#">
${data.user.email}
</a>
<div class="dropdown-menu p-3 dropdown-menu-right">
<div class="form-group">
<label for="email1">Logged in as:</label>
<p>${data.user.email}</p>
</div>
<button onClick="getLogout()" ">Sign out</button>
</div>
`

Add each line of the code to a variable and then write the variable to your inner HTML. See below:
var div = document.createElement('div');
div.setAttribute('class', 'post block bc2');
var str = "First Line";
str += "Second Line";
str += "So on, all of your lines";
div.innerHTML = str;
document.getElementById('posts').appendChild(div);

If you are using on the same domain then you can create a seperate HTML file and then import this using the code from this answer by #Stano :
https://stackoverflow.com/a/34579496/2468603

By far the easiest way is to use the insertAdjacentHTML() method.
w3schools article

Just make sure to wrap you LARGE CODE INTO A SINGLE DIV like a wrapper, then it doesn't matter how long it is.
HTML:
<div id='test'></div>
JS:
const arr = ['one', 'two', 'three'];
let mapped = arr.map(value=> {
return `
<div>
<hr />
<h1>${value}</h1>
<h3>this is it</h3>
</div>
`
});
document.querySelector('#test').innerHTML = mapped.join('');

Related

replaceAll() in JavaScript failed to find </em> in HTML page

I am not familiar with JavaScript and html. But I tried to implement a function using JavaScript.
I want to replace all <em> and </em> in a html page. So I insert a piece of javascript code in the page:
function rep()
{
document.body.innerHTML
= document.body.innerHTML
.replaceAll("<em>", "_");
document.body.innerHTML
= document.body.innerHTML
.replaceAll("</em>", "_");
}
window.onload=rep()
<!DOCTYPE html>
<html lang="en">
<!-- ... -->
<article>
<div class="container">
<div class="row">
<div class="col-lg-8 col-lg-offset-2 col-md-10 col-md-offset-1 post-container">
<p>(Weierstrass) 设 $z_{0}$ 是 $f$ 的本性奇点,那么对任意 $A \in \mathbb{C}<em>{\infty}$, 必存在趋于 $z</em>{0}$ 的点列 $\left{z_{n}\right}$, 使得 $\lim <em>{n \rightarrow \infty} f\left(z</em>{n}\right)=A$.</p>
</div>
</div>
</div>
<!-- ... -->
</html>
It succeeded in replacing <em> with "_", but all </em> did not change. What's wrong with the code?
Thank you!
Let's see what happens when browsers see invalid html like:
test</em>
console.log(document.body.innerHTML)
test</em>
The above prints test (and the script)
That's because the browser strips invalid structures when parsing
When you do
document.body.innerHTML
= document.body.innerHTML
.replaceAll("<em>", "_");
You replace all <em> tags correctly, but the closing tags are removed
This will work on the other hand:
document.body.innerHTML = document.body.innerHTML
.replaceAll("<em>", "_")
.replaceAll("</em>", "_");
<em>test</em>
It maybe better to use the available DOM methods for this.
Pick up all the em elements with querySelectorAll.
For each element create a text node. Bookend the element's original text content with underscores, and add that to the text node. Use replaceWith to replace the em element with the text node.
const ems = document.querySelectorAll('em');
ems.forEach(em => {
const text = `_${em.textContent}_`;
const node = document.createTextNode(text);
em.replaceWith(node);
});
<p>(Weierstrass) 设 $z_{0}$ 是 $f$ 的本性奇点,那么对任意 $A \in \mathbb{C}<em>{\infty}$, 必存在趋于 $z</em>{0}$ 的点列 $\left{z_{n}\right}$, 使得 $\lim <em>{n \rightarrow \infty} f\left(z</em>{n}\right)=A$.</p>
<ul>
<li><em>This is some italised text</em></li>
<li>And this is not.</li>
<li><em>But this is</em>.</li>
</ul>
Additional documentation
querySelectorAll
replaceWith
forEach
Template/string literals
Processing html with regexes or string functions is a bad idea (html is not a string), but if you must, it should be done like this:
let html = document.body.innerHTML
html = html.replace(...)
html = html.replace(...) etc
document.body.innerHTML = html
In other words, do not use a partially processed string to set innerHTML.
Simpler but not efficient:
document.body.innerHTML.replace(/\<em\>|\<\/em\>/gm, '_');
Result:
//body before: <em>test</em>
//body after: _test_
The regex will pass over the entire body and will replace all <em> or </em> occurrences with _
The regex options g for global and m for multiline allow to cover the whole body and multiple occurrences.

Suppress rendering of html from template-literals within another template-literal

I'd like to display some html code that is within a template literals within another template literal.
I am using <pre><code> tags to attempt this.
The problem I'm having at the moment is that the browser renders the html rather than its code. For example it displays an actual input box in place of displaying the code, e.g. <input ...
I'd like to apply some reversible process using javascript to the templateliteral variable so that the html is displayed literally (it should display all orange on black text in the below snippet), and so that the original code can be easily got back.
What might be a good way to do this?
Here is an example of the problem (it should display all orange on black text):
let templateliteral = `let inner = { content : \`
<style>
#exampleDivInTemplateLiteral { background-color:lightblue;color:white; }
</style>
<div id="exampleDivInTemplateLiteral">
<b>this is some text.</b>
<input id="exampleInput" placeholder="example input">\</input>
</div>\`}`
document.getElementById("exampleDiv").innerHTML = '<pre><code>' + templateliteral + '<pre></code>'
<div id="exampleDiv" style="background-color:black;color:orange;">
<div>
Note this is specific to template literals within template literals.
Posting in edited form so I can post answer
I used createElement and innerText (in place of innerHTML):
let templateliteral = `let inner = { content : \`
<style>
#exampleDivInTemplateLiteral { background-color:lightblue;color:white; }
</style>
<div id="exampleDivInTemplateLiteral">
<b>this is some text.</b>
<input id="exampleInput" placeholder="example input">\</input>
</div>\`}`
let divel = document.getElementById("exampleDiv")
let preel = document.createElement("pre");
let codeel = document.createElement("code");
preel.appendChild(codeel)
divel.appendChild(preel)
codeel.innerText = templateliteral
<div id="exampleDiv" style="background-color:black;color:orange;">
<div>

Adding "literal HTML" to an HTML widget with data from a datasource?

When creating an HTML widget in FreeBoard, the following text is displayed:
Can be literal HTML, or javascript that outputs HTML.
I know I can do the following to return HTML with data, but if I want to do something more complex I'd prefer to use literal HTML
return html with data
return "<div style='color: red'>This is a red timestamp: " + datasources["DS"]["Timestamp"] + "</div>"
literal html with no data
<div style='color: red'>
This is red text.
</div>
<div style='color: blue'>
This is blue text.
</div>
Both of those work. My question is, how can I insert data from a datasource into the literal html example?
For more context, here is what is at the top of the editor:
This javascript will be re-evaluated any time a datasource referenced here is updated, and the value you return will be displayed in the widget. You can assume this javascript is wrapped in a function of the form function(datasources) where datasources is a collection of javascript objects (keyed by their name) corresponding to the most current data in a datasource.
And here is the default text:
// Example: Convert temp from C to F and truncate to 2 decimal places.
// return (datasources["MyDatasource"].sensor.tempInF * 1.8 + 32).toFixed(2);
I don't know the freeboard framework, but a generic solution would be to use HTML5 templates, if your browser support requirements allow it.
function supportsTemplate() {
return 'content' in document.createElement('template');
}
if (supportsTemplate()) {
alert('browser supports templates');
} else {
alert('browser does not support templates');
}
var template = document.querySelector('#timestamp-template');
var timestamp = template.content.querySelector('.timestamp');
timestamp.innerHTML = new Date().toLocaleString();
var clone = document.importNode(template.content, true);
var output = document.querySelector('#output');
output.appendChild(clone);
<template id="timestamp-template">
<div style='color: red' class="timestamp">
This is default red text.
</div>
<div style='color: blue'>
This is blue text.
</div>
</template>
<div id="output"></div>
You would obviously need to adapt this strategy to support whatever data sources and transforms your project needs.
Failing support for HTML5 template elements, you could also use <script type="text/template">.
Here is an example of how to insert a Datasource into an HTML widget:
var LVL = datasources["GL"]["Level"];
return `<div style="width: 200px; height: 200px;background:rgb(242,203,56);"></style>
<svg width=200 height=`+LVL+`><rect width=100% height=100% fill=grey></svg>
</div>`;

a more graceful multi-line javascript string method

The only way I know how to print a huge string without using += is to use \ backslashes. ugly!
<div id="foo"></div>
<script type="text/javascript">
var longString = '<div id="lol">\
<div id="otherstuff">\
test content. maybe some code\
</div>\
</div>';
document.getElementById('foo').innerHTML = longString;
</script>
is there any way to do this where the longString is untainted? php has $foo = ''' long multiline string '''; I want this in javascript!
Anyone know of a better method for printing long, multi-line strings in javascript?
In general, the answer is: not in the language syntax. Though as Ken pointed out in his answer there are many work-arounds (my personal method is to load a file via AJAX). In your specific case though, I'd prefer creating a HTML constructor function so you can then define the HTML structure using javascript object literals. Something like:
var longString = makeHTML([{
div : {
id : "lol",
children : [{
div : {
id : "otherstuff",
children : [{
text : "test content. maybe some code"
}]
}]
}]
which I find to be much easier to handle. Plus, you this would allow you to use real function literals when you need it to avoid string quoting hell:
makeHTML([{
span : {
onclick : function (event) {/* do something */}
}
}]);
note: the implementation of makeHTML is left as exercise for the reader
Additional answer:
Found some old code after a quick scan through my hard disk. It's a bit different from what I suggested above so I thought I'd share it to illustrate one of the many ways you can write functions like this. Javascript is a very flexible language and there is not much that forces you to write code one way or another. Choose the API you feel most natural and comfortable and write code to implement it.
Here's the code:
function makeElement (tag, spec, children) {
var el = document.createElement(tag);
for (var n in spec) {
if (n == 'style') {
setStyle(el,spec[n]);
}
else {
el[n] = spec[n];
}
}
if (children && children.length) {
for (var i=0; i<children.length; i++) {
el.appendChild(children[i]);
}
}
return el;
}
/* implementation of setStyle is
* left as exercise for the reader
*/
Using it would be something like:
document.getElementById('foo').appendChild(
makeElement(div,{id:"lol"},[
makeElement(div,{id:"otherstuff"},[
makeText("test content. maybe some code")
])
])
);
/* implementation of makeText is
* left as exercise for the reader
*/
One technique if you have a big block is a <script> tag with an invalid type. It will be ignored by browsers.
<script type="text/x-my-stuff" id="longString">
<div id="lol">
<div id="otherstuff">
test content. maybe some code
</div>
</div>
</script>
<script type="text/javascript">
var longString = document.getElementById("longString").text;
document.getElementById('foo').innerHTML = longString;
</script>
A few somewhat unattractive options are discussed in the answers to this question.
You really could minimize this ugliness by creating your <div id="lol"> as HTML, and set its content with .innerHTML = "test content. maybe some code"
I don't like creating HTML in Javascript because of this exact issue, and instead use "template" elements which i simply clone then manipulate.
var lol = document.getElementById("template_lol").clone();
lol.firstChild.innerHTML = "code and stuff";
foo.appendChild(lol);
And this is the HTML:
<body>
<div>normal stuff</div>
<div style="display:none" id="templateBucket">
<div id="template_lol"><div class="otherstuff"></div></div>
</div>
</body>
This works too :
var longString =
'<div id="lol">' +
'<div id="otherstuff">' +
'test content. maybe some code' +
'</div>' +
'</div>';

Is it possible to generate end comments for html

Is it possible to generate comments for closing div tags, lets take this ex. into consideration normal HTML:
<div id="content">
...
...buch of html or whateve
</div>
with comments :
<div id="content">
...
...buch of html or whateve
</div><!--End of content-->
and so on go trough each div element and comment the end of it ?
Here is a possible solution, in PHP, using DOM :
Using PHP allows you to save it, or whatever you need to ; and as it's using DOM, which is quite standardized, translating this to another language shouldn't require too much work.
(And, judging from a comment on your question, you didn't exclude other languages that JS)
$html = <<<HTML
<div id="content">
...
...buch of html or whateve
</div>
HTML;
$dom = new DOMDocument();
$dom->loadHTML($html);
$divs = $dom->getElementsByTagName('div');
for ($i = $divs->length - 1 ; $i > -1 ; $i--) {
$div = $divs->item($i);
if ($div->hasAttribute('id')) {
$id = $div->getAttribute('id');
$comment = $dom->createComment("End of {$id}");
if($div->nextSibling) {
$div->parentNode->insertBefore($comment, $div->nextSibling);
} else {
$div->parentNode->appendChild($comment);
}
}
}
echo $dom->saveHTML();
Which gets you the following HTML source :
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
<html><body>
<div id="content">
...
...buch of html or whateve
</div>
<!--End of content-->
</body></html>
A couple of things to note :
DOM allows one to load and parse non-valid HTML
And generates valid-HTML
And, about what this does :
Load the HTMl string, using DOMDocument
Search for all <div> tags
Foreach <div> tag :
If it has an id attributes,
Get its value
Create a comment based on that value
And add it to the DOM, after the </div> tag
Another solution, thinking about it, would probably have been to use XPath, instead of getElementsByTagName + hasAttribute...
using jQuery, this is very simple.
jQuery('div').after('<!--end of content-->');
EDIT:
jQuery('div').each(function(){ jQuery(this).after('<!-- end of '+jQuery(this).id + '-->');});
var divs = document.getElementsByTagName("div");
for (var d = divs.length-1; d >= 0; --d) {
var div = divs[d];
var id = div.id; // d.getAttribute("id")
if (id) {
var cmt = document.createComment("End of " + id);
div.parentNode.insertBefore(cmt, div.nextSibling);
}
}

Categories