Indent created Elements in javascript - javascript

So I've used javascript to build up an HTML markup for a component (accordion)
const accordItem = document.createElement("div");
const accordTitle = document.createElement("p");
const accordContent = document.createElement("div");
const accordContentParagraph = document.createElement("p");
All the elements are appended to a container
container.append(accordItem);
accordItem.append(accordTitle, accordContent);
accordContent.append(accordContentParagraph);
In the code, I populate the divs and paragraphs with content. So, an accordion generator of a sort. So the imaginary user can see a preview of how it would look. And also download a text file.
But if I copy the code from developers' tools. Or write it in the file; I see this mess of a markup. Everything is in one line.
<div class="accordionItem"><p class="accordionTitle">This is some content</p><div class="accordionContent"><p>This is additional content! Something Something</p></div></div><div class="accordionItem"><p class="accordionTitle">This is some content, more</p><div class="accordionContent"><p>Moremore</p></div></div>
While not really a problem, I would like to learn why that is. (pretty new at this).
And is there an easy way to make it indented?
like so:
<div class="accordionItem">
<p class="accordionTitle">This is some content</p>
<div class="accordionContent">
<p>This is additional content! Something Something</p>
</div>
</div>
<div class="accordionItem">
<p class="accordionTitle">This is some content, more</p>
<div class="accordionContent">
<p>Moremore</p>
</div>
</div>

instead of using create element method. You can assign a variable with html string and just append the string with parent Element using insertAdjacentHTML method.
Example :
let accordionElem =
`<div id="accordionCont">
<span id="childElem1">Hi</span>
<span id="childElem2">Hello</span>
</div>`;
document.getElementById("parentCont").insertAdjacentHTML("afterbegin",accordionElem);
<!DOCTYPE html>
<html lang="en">
<head>
<title>InsertAdjacentHTML</title>
</head>
<body>
<div id="parentCont"></div>
</body>
</html>
MDN DOC

Related

Can I add div contents after defining a div child using JavaScript?

Can I add div contents (innerText / textContent) after adding a div in the parent class using JavaScript. I know it's confusing and also I am not sure whether my demand is correct or not.
Can someone just help how to achieve below HTML results using JS.
<div class="container">
<div class="mesg left">
<div class="username">
You
</div>
The main content goes here
<span id="time">08:23am</span>
</div>
</div>
I had tried to achieve this but I my case the content comes up... Like(see the output html from what I have did from JS)
<div class="container">
<div class="description">
The main content goes here
<div class="name">
James
</div>
<span id="time">08:23am</span>
</div>
</div
You can use Node.insertBefore to place the text before the timestamp.
Given your HTML this would work:
const time = document.getElementById("time")
const content = document.createElement("p")
content.innerText = "The main content goes here"
document.getElemenstByClassName("mesg left")[0]
.insertBefore(content, time)

Dynamic Div Tags in a While Loop

I have a while loop that populates 4 div tabs (in one parent div) with PHP.
I also have a copy-to-clipboard script to copy text from a P tag. Unfortunately, this function works only if the tag and button have a unique Id.
So my question is this:
How do I assign a sequential Id to my looped P tags? In the sense that I want the first set of tabs in the first populated div to have Ids 1,2,3,4, and the next one to have 5,6,7,8...and so on.
Here is the HTML in the PHP loop:
<div id='content' class='tab-content'>
<div id='navpills-1' class='tab-pane active'>
<p id='text'>Text to be copied to clipboard.</p>
<button id='jsbtn' class='btn jsbtn' onclick='copyToClipboard('p#text')'>Copy</button>
</div>
</div>
Here is the script I'm using to copy text from the P tag to the clipboard:
<script>
function copyToClipboard(element) {
var $temp = $("<textarea>");
var brRegex = /<br\s*[\/]?>/gi;
$("body").append($temp);
$temp.val($(element).html().replace(brRegex, "\r\n")).select();
document.execCommand("copy");
$temp.remove();
}
</script>
If you are not committed to using jQuery then you can easily accomplish your goal with some fairly basic, vanilla Javascript. The Clipboard API provides the modern solution for copying text (& other things) to & from the system clipboard.
In general working with IDs can become troublesome as they need to be unique within the DOM so where the same functionality needs to be applied to multiple it items you must try to do what you were initially hoping to do - assign sequential IDs or some other such solution and reference that ID in the function call. This does not scale well and better options exist.
The following uses no ID attributes anywhere - instead the event handler assigned to each button uses the event to identify which element was clicked ( invoked the handler ) and from that node it is possible, given that you know the DOM structure, to find other nodes of interest. In the code below the event.target refers to the button - from there we find it's parent and from that parent we find the specific P element whose content is to be copied. The method suggested by #CBroe does this using jQuery methods - something I'm not familiar with.
<!DOCTYPE html>
<html lang='en'>
<head>
<meta charset='utf-8' />
<title>Basic Clipboard Copy Example</title>
</head>
<body>
<div class='tab-content'>
<div class='tab-pane active'>
<p class='copy'>Text to be copied to clipboard.</p>
<button class='btn jsbtn'>Copy</button>
</div>
<div class='tab-pane active'>
<p class='copy'>Some other text to be copied to clipboard.</p>
<button class='btn jsbtn'>Copy</button>
</div>
<div class='tab-pane active'>
<p class='copy'>Guess what...more text and not an ID in sight</p>
<button class='btn jsbtn'>Copy</button>
</div>
<div class='tab-pane active'>
<p class='copy'>And the final Paragraph and final chapter and final word.</p>
<button class='btn jsbtn'>Copy</button>
</div>
</div>
<textarea cols=100 rows=10></textarea>
<script>
document.querySelectorAll('button.jsbtn').forEach( bttn=>bttn.addEventListener('click',e=>{
const pttn=/<br\s*[\/]?>/gi;
const paragraph=e.target.parentNode.querySelector('p.copy');
const textarea=document.querySelector('textarea');
// writeText copies text to the clipboard
navigator.clipboard.writeText( paragraph.innerHTML.replace( pttn, String.fromCharCode( 10 ) ) )
.then(
()=>{
/*
All good... and to illustrate reading this copied text...
readText - reads from the clipboard
*/
navigator.clipboard.readText()
.then( text=>{
textarea.value=text;
})
},
(e)=>{console.log( 'error: %s',e.message )}
)
}));
</script>
</body>
</html>

Div placed using JavaScript are not displayed in the correct place

I have a problem with inserting a specific element using JavaScript.
First of all, my website structure is as follows.
<div class="woocommerce-billing-fields__field-wrapper">
<p id="billing_email_field">..</p>
<p id="billing_country_field">..</p>
<p id="billing_first_name_field">..</p>
<p id="billing_postcode_find_field">..</p>
<p id="billing_address_1_field">....</p>
</div>
I want to add a Wrap element above the billing_address_1_field element.
So I put the Warp element using the code below.
<script>
//빌링에 wrap 엘리멘트 추가
const parent = document.querySelector('#woocommerce-billing-fields__field-wrapper');
const billingField1 = document.querySelector('#billing_address_1_field');
const newDiv = document.createElement('div');
newDiv.setAttribute('id', 'wrap');
newDiv.style.cssText = 'display:black;width:100%;margin:5px 0;';
parent.insertBefore(newDiv, billingField1);
</script>
However, when using this code, the warp will be placed on top of the div element.
<div class="woocommerce-billing-fields__field-wrapper">
<div class="wrap"></div>
<p id="billing_email_field">..</p>
<p id="billing_country_field">..</p>
<p id="billing_first_name_field">..</p>
<p id="billing_postcode_find_field">..</p>
<p id="billing_address_1_field">....</p>
</div>
Can someone please tell me how i can solve this problem?
I'd like to use insertBefore instead of prepend due to compatibility issues with IE.
Thank you.
On your script please add a dot(.) instead of hash(#) :
const parent = document.querySelector('.woocommerce-billing-fields__field-wrapper');
hope it will help. Thanks

Partially run code as html and as text

Introduction
I'm currently creating a templatebuilder where users can build a template for an app. The user can drag and drop multiple blocks, such as text blocks and 'custom code' blocks. The template will be parsed within an app. Right now, a template could look like this:
<section>
<div class="row">
<div class="col-sm-12">
<section data-type="code">
<#code></#code>
</section>
</div>
</div>
<div class="row">
<div class="col-sm-12" data-type="container-content">
<section data-type="text">
<u>Lorem</u> ipsum
</section>
</div>
</div>
</section>
So, this template contains two elements (see the data-type attribute): one part is custom written code. Here, the user wrote custom code, including Apache Freemarker code. The second part is custom written text.
Situation
The code above will be used in two different ways.
Exactly this code will be used inside an app that's using the
template (so that's why they should be able to write Freemarker code,
because this will be parsed).
On my website, the user should be able
to edit this template. Because the code is stored in the database as
written above, there is a problem:
Problem
When I directly render the template in the web-interface, the text part will render correctly with the <u></u> tags, but the code part will be rendered as html as well which might cause weird behaviour (such as the freemarker notation </#list> being auto-converted to <!--#list-->).
But, if I render the full template as text only, the text part with the <u></u> tags will not be rendered as well.
Expected outcome
I want to read the template variable with JavaScript / jQuery and then parse each data-type with text as html, and with code as text.
How can I loop through the template and do this?
There is an alternative syntax that uses square brackets instead of angle brackets.
Check if it solves your tag identifying problem without messing any other feature.
https://freemarker.apache.org/docs/dgui_misc_alternativesyntax.html
EDIT 1
To show the source code inside the <#code> tags when the HTML is parsed, you could escape it in your database (escape html special chars like <, > and & to < > and &). So, when it is rendered, no html tags will be created in the code content and the document won't be messed up.
Then, you can render all the content of the database directly as HTML: text will keep markup and code will be text.
To do that modification, you can use regular expressions to find what is enclosed by <#code> tags and replace with the HTML-escaped equivalent. The exact way to do it depends on the language you will be using for the job, as there are some differences in RegExes and in the available escape funcions.
EDIT 2
If you are loading the content using AJAX, you have the chance of applying the replace in javascript, AFTER the content was obtained from the server, keeping your database as it is.
Recap of the problem
For parsing HTML in javascript, you generally use a DOMParser object (supported by IE10+).
Like you said, parsing fails inside the data-type="code" section, because it does not know how to handle the </#...> tags...
const templ = `<section><div class="row"><div class="col-sm-12"><section data-type="code"><#code></#code></section></div></div><div class="row"><div class="col-sm-12" data-type="container-content"><section data-type="text"><u>Lorem</u> ipsum</section></div></div></section>`;
const parser = new DOMParser();
const doc = parser.parseFromString(templ, "text/html");
console.log(
"Wrongly parsed </#code> tag:\n",
doc.querySelector("[data-type='code']").innerHTML
);
Finding a way around
Now, it might sound like a good idea to try and do a quick regex find-and-replace on the characters that need to be escaped, but I wouldn't recommend it...
As far as I know, there's no way to "break in" to the parsing process or pass a strategy for certain types of elements...
I'd say this leaves you with two options. Either:
Don't use the unparsable syntax inside the code section, as suggested by user Eduardo Poço in their answer
or, (my prefered direction), try to
Modify the template itself to stop parsing the contents of the code sections all together
Using a modified template
There's a tag for "script" like content in HTML! It's, unsurprisingly, the <script> tag. Let's inject it in our code section:
<section data-type="code">
<script type="text">
<#code></#code>
</script>
</section>
The DOMParser won't touch this tag, leaving it exactly as is:
const templ = '<section><div class="row"><div class="col-sm-12"><section data-type="code"><script type="text"><#code></#code></' + 'script></section></div></div><div class="row"><div class="col-sm-12" data-type="container-content"><section data-type="text"><u>Lorem</u> ipsum</section></div></div></section>';
const parser = new DOMParser();
const doc = parser.parseFromString(templ, "text/html");
console.log(
"Now, there's a <script> tag:\n",
doc.querySelector("[data-type='code']").innerHTML
);
Note that I had to join the template string from two parts to make sure stackoverflow's snippet doesn't break. Are they experiencing a similar issue? :-o
Now, all we have to do is use the general DOM methods, including innerText (not innerHTML) to get the script's inner content back in to the visible part of the DOM:
var templ = '<section><div class="row"><div class="col-sm-12"><section data-type="code"><script type="text"><#code></#code></' + 'script></section></div></div><div class="row"><div class="col-sm-12" data-type="container-content"><section data-type="text"><u>Lorem</u> ipsum</section></div></div></section>`;'
var parser = new DOMParser();
var doc = parser.parseFromString(templ, "text/html");
Array
.from(doc.querySelectorAll(
"[data-type='code'] > script")
)
.forEach(script => {
const codeTag = document.createElement("code");
codeTag.innerText = script.innerHTML;
script.replaceWith(codeTag);
});
document.getElementById("wrapper").appendChild(doc.body.firstChild);
code { background: #efefef; }
<div id="wrapper"></div>
You could use charset codes, so it does not execute before outputting.
HTML charset reference
They can edit it, since it appears normal and send it back to you or server. Make sure you include your charset reference in the head.
<meta charset="UTF-8"> // HTML5
<meta http-equiv="Content-Type" content="text/html;charset=ISO-8859-1"> // HTML4
<!-- #CHARSET / No execute -->
<section>
<div class="row">
<div class="col-sm-12">
<section data-type="code">
<#code> <!-- Run this --> </#code>
</section>
</div>
</div>
<div class="row">
<div class="col-sm-12" data-type="container-content">
<section data-type="text">
<u> <!-- Don't run --> </u>
</section>
</div>
</div>
</section>
If i didn't misunderstand it, you can use <plaintext> tag for rendering block as text on the page.
<plaintext>
<#code></#code>
</plaintext>
What about something like this?
// https://stackoverflow.com/questions/7477/autosizing-textarea-using-prototype
function FitToContent(id, maxHeight)
{
var text = id && id.style ? id : document.getElementById(id);
if (!text)
return;
/* Accounts for rows being deleted, pixel value may need adjusting */
if (text.clientHeight == text.scrollHeight) {
text.style.height = "30px";
}
var adjustedHeight = text.clientHeight;
if (!maxHeight || maxHeight > adjustedHeight)
{
adjustedHeight = Math.max(text.scrollHeight, adjustedHeight);
if (maxHeight)
adjustedHeight = Math.min(maxHeight, adjustedHeight);
if (adjustedHeight > text.clientHeight)
text.style.height = adjustedHeight + "px";
}
}
$('textarea').each(function(i,v){
FitToContent($(v)[0], document.documentElement.clientHeight)
});
textarea {
background: transparent;
border: 0;
font-family: 'Times New Roman';
font-size: 1em;
width: 100%;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<section>
<div class="row">
<div class="col-sm-12">
<h4>First code block</h4>
<section data-type="code">
<textarea class='code'><#code>
<h2>FreeMarker Spring MVC Hello World</h2>
<table class="datatable">
<tr>
<th>Make</th><th>Model</th>
</tr>
<#list model["carList"] as car>
<tr>
<td>${car.make}</td>
<td>${car.model}</td>
</tr>
</#list>
</table>
</#code></textarea>
</section>
</div>
</div>
<div class="row">
<div class="col-sm-12" data-type="container-content">
<h4>Regular HTML section</h4>
<section data-type="text">
<u>Lorem</u> ipsum
</section>
</div>
</div>
<div class="row">
<div class="col-sm-12">
<h4>Second code block</h4>
<section data-type="code">
<textarea class='code'><#code>
<table class="datatable">
<tr>
<th>Name</th><th>Email</th>
</tr>
<#list model["personList"] as person>
<tr>
<td>${person.name}</td>
<td>${person.email}</td>
</tr>
</#list>
</table>
</#code></textarea>
</section>
</div>
</div>
</section>

body.innerHTML before/after nth-child

Is it possible to add something with innerHTML before/after the nth child of <body>?
e.g.:
<html>
<head></head>
<body>
<div id="first">First</div>
<div id="second">Second<div id="second_sub"></div></div>
<div id="third">Third</div>
</body>
</html>
I can add at the beginning with body.innerHTML = html + body.innerHTML and at the end with body.innerHTML += html but how to add, for example, before the second <div>?
I don't want to use replace on <div id="second"> as the source changes and the insert should be done at random. Is there a solution with innerHTML or do I need to switch to Dom Nodes? Would be slow in old browsers :(
You are probably looking for the insertBefore method. It will insert a child before the given element. Alternatively there's the appendChild method which will always push elements on the beginning of the given element.
Examples:
<body>
<span id="elem1">Hello</span>
<span id="elem2">World</span>
</body>
Let's assume we're inserting a new element stored in the var newElem:
document.insertBefore(newElem, document.getElementById("elem2")) will give:
<body>
<span id="elem1">Hello</span>
<!-- New element here! -->
<span id="elem2">World</span>
</body>
document.appendChild(newElem) will give:
<body>
<!-- New element here! -->
<span id="elem1">Hello</span>
<span id="elem2">World</span>
</body>
I'm using this by now (Thanks to ajax333221):
e = document.createElement('div');
e.innerHTML = '<div>... huge content with several sub elements ...</div>';
document.body.insertBefore(e, document.body.childNodes[2]);
This is a combination of both techniques. With this I'm able to use the fast .innerHTML without extremely blowing up the code with createElement(), setAttribute(), etc.
Other solutions are welcome.

Categories

Resources