How to Apply CSS to Dynamic JS Elements without In-line Styling - javascript

I'm new to web development and I've been trying to apply styling to div elements that are generated using javascript, but I've only been able to do this manually using the following function:
function addStyle(element) {
rem = element.style;
rem.fontSize = "16px";
rem.background = "#fff";
rem.color = "#333";
}
This works fine for individual elements, but there might be potentially dozens of dynamic elements created which all must include the same inline styling. I've read elsewhere that apparently this is not a good practice and should be avoided if possible. Thus, I would prefer that these css rules are defined in a separate file so that I can potentially access them for other elements as well.
However, I have been unable to find a solution anywhere online no matter how similar their issue may seem.
Here's my relevant HTML code:
<h3 id="kudos_title">Kudos</h3>
<div class="remarks"></div>
My JS to create new div element:
function addElement (i) {
// create a new div element
const newDiv = document.createElement("div");
// Add message, author, and source to content
const content = document.createTextNode('"' + `${msg[i].remark}` + '"' + " - " + `${msg[i].author}` + " from " + `${msg[i].source}`);
// add the text node to the newly created div
newDiv.appendChild(content);
addStyle(newDiv, i);
// add the newly created element and its content into the DOM
const current_remark = document.querySelector(".remarks");
document.body.insertBefore(newDiv, current_remark);
}
Lastly, the CSS:
#kudos_title {
text-align: center;
font-family: Spectral, serif;
font-size: 50px;
}
.remarks {
padding: 20px;
color: pink;
background-color: springgreen;
}
I should mention that the heading with id=kudos_title is successfully styled, but anything part of the remarks class is not. So clearly the .css file is being recognized for static elements, but JS created divs are not.

You are using insertBefore which will insert the element before the target (thus NOT inserting inside it). Try appending instead. Additionally, it's best practice to use HTML entities for things like quotes in text, so I used them here, showing how you can combine your string using your template literals. To add certain classes, use element.classList.add()
let msg = [{
remark: "test",
author: "they",
source: "there"
}];
function addElement(i) {
// create a new div element
const newDiv = document.createElement("div");
// Add message, author, and source to content
const content = `"${msg[i].remark}" - ${msg[i].author} from ${msg[i].source}`;
// add the text node to the newly created div
newDiv.innerHTML = content;
newDiv.classList.add('special');
// add the newly created element and its content into the DOM
const current_remark = document.querySelector(".remarks");
current_remark.append(newDiv);
}
addElement(0);
#kudos_title {
text-align: center;
font-family: Spectral, serif;
font-size: 50px;
}
.remarks {
padding: 20px;
color: pink;
background-color: springgreen;
}
.special {
color: #f00;
font-weight: bold;
}
<h3 id="kudos_title">Kudos</h3>
<div class="remarks"></div>

If you want all the new divs to be styled in the same way, you can just give them a class and then define those styles in a CSS file.
You can do it like this: note that the name "myclass" is given purely for illustration, I'm sure you can come up with a meaningful name that works for your application:
JS
function addElement (i) {
// create a new div element
const newDiv = document.createElement("div");
// Add message, author, and source to content
const content = document.createTextNode('"' + `${msg[i].remark}` + '"' + " - " + `${msg[i].author}` + " from " + `${msg[i].source}`);
// add the text node to the newly created div
newDiv.appendChild(content);
// add CSS class
newDiv.classList.append("myclass");
// add the newly created element and its content into the DOM
const current_remark = document.querySelector(".remarks");
document.body.insertBefore(newDiv, current_remark);
}
CSS
.myclass {
font-size: 16px;
background: #fff";
color: #333;
}

Just use a class and include it when making the element
JS:
newDiv.className = 'bar';
CSS:
.dynamicElement{
padding: 20px;
color: pink;
background-color: springgreen;
}
You can just use a css file

Related

How do I create a new HTML element from JavaScript? [duplicate]

I want to dynamically create some HTML elements (3 html element) and then return this html code as a string in a variable. I don't want to write the HTML code in the following function to some div, but, I want to return it in a var.
function createMyElements(id1,id2,id3){
//create anchor with id1
//create div with id 2
//create xyz with id3
//now return the html code of above created just now
}
How can I do this?
[Edit 2021/10] This answer is now > 10 years old. Here is a snippet containing several ways to create and/or inject elements. The answer for the question asked (create some element(s) and retrieve their html code) can be found # the bottom of the snippet.
// The classic createElement
// -------------------------
// create a paragraph element using document.createElement
const elem = document.createElement(`p`);
elem.id = `myBrandnewDiv1`;
// put in some text
elem.appendChild(document.createTextNode(`My brand new div #1`));
// append some html (for demo, preferrably don't use innerHTML)
elem.innerHTML += ` => created using
<code>document.createElement</code>`;
// append a new paragraph within #myBrandNewDiv1
const nested = elem.appendChild(document.createElement(`p`));
nested.classList.add(`nested`);
// add some text to that
nested.textContent = `I am nested!`;
// the elements are still in memory, now add the
// whole enchillada to the document
document.body.appendChild(elem);
// insertAdjacentHTML
// ------------------
// nest an element within the nested div
nested.insertAdjacentHTML(`afterbegin`,
`<div id="nestedWithin#nested">
This text will appear <i>above</i> the text of
my parent, that being div#nested.
Someone had the nerve to insert me using
<code>insertAdjacentHTML</code>
</div>`);
// Object.assign
// -------------
// Use Object.assign to create an element and
// assign properties/html to it in one go
const newElem = Object.assign(
document.createElement(`div`),
{ id: `myBrandnewDiv2`,
innerHTML: `div#myBrandnewDiv2 signing in.
I was <i>assigned</i> using <code>Object.assign</code>…`});
document.body.appendChild(newElem);
// insertAdjacentElement combined with Object.assign
// -------------------------------------------------
// use the above technique combined with insertAdjacentElement
newElem.insertAdjacentElement(
`beforeend`,
Object.assign(document.createElement(`span`),
{ id: `myBrandnewnested2_nested`,
innerHTML: `<br>Me too! And appended I was
with <code>insertAdjacentElement</code>` })
);
// createDocumentFragment
// ----------------------
// Use a document fragment to create/inject html
const fragment = document.createDocumentFragment();
const mdnLnk = `https://developer.mozilla.org/en-US/` +
`docs/Web/API/Document/createDocumentFragment`;
fragment.appendChild(
Object.assign(
document.createElement(`p`),
{innerHTML: `Regards from <code>createDocumentFragment</code>
(see MDN)`})
);
document.querySelector(`#myBrandnewDiv2`).appendChild(fragment);
// Create, but don't inject
// ------------------------
const virtual = Object.assign(
document.createElement(`p`),
{ innerHTML: `
id1
<div id="id2">Hi!</div>
<p id="id3">Hi 2!</p>`,
classList: [`xyz`], } );
const prepareHtml4Reporting = html =>
html.replace(/</g, `<`)
.replace(/\n\s+/g, `\n`)
.replace(/\n\n/g, `\n`);
document.body.insertAdjacentHTML(
`beforeend`,
`<h3>html only</h3><pre>${
prepareHtml4Reporting(virtual.innerHTML)}</pre>`);
body {
font: normal 12px/15px verdana, arial, sans-serif;
margin: 2rem;
}
code {
background-color: #eee;
}
.nested {
margin-left: 0.7rem;
max-width: 450px;
padding: 5px;
border: 1px solid #ccc;
}
I have used some of these methods in this library (see /src/DOM.js), with a mechanism for sanitizing html before it is injecting.
Html:
<div id="main"></div>
JavaScript:
var tree = document.createDocumentFragment();
var link = document.createElement("a");
link.setAttribute("id", "id1");
link.setAttribute("href", "http://site.com");
link.appendChild(document.createTextNode("linkText"));
var div = document.createElement("div");
div.setAttribute("id", "id2");
div.appendChild(document.createTextNode("divText"));
tree.appendChild(link);
tree.appendChild(div);
document.getElementById("main").appendChild(tree);
The main reason to use a documentFragment in stead of just adding the elements directly is speed of execution.
At this size it doesn't matter, but when you start adding hundreds of elements, you will appreciate doing it in-memory first :-)
With documentFragment you can construct a whole tree of DOM-elements in-memory and will not afffect the browser DOM untill the last moment.
Otherwise it forces the browser to update for every element, which sometimes can be a real pain to watch.
If you're doing this repeatedly (dynamically creating HTML), you might want to use a more general approach.
If you want to create three unrelated elements, you can do:
var anchor = elem("a", {"id":"id1"});
var div = elem("div", {"id":"id2"});
var xyz = elem("div", {"id":"id3"});
Now, you have three elements. If you want to get the HTML of these (as string), simply do:
var html = anchor.outerHTML + div.outerHTML + xyz.outerHTML;
If you want to have these three in an element (say, div), do:
var div = elem("div", null, [
elem("a", {"id":"id1"}),
elem("div", {"id":"id2"}),
elem("div", {"id":"id3"}),
]);
You can get the HTML with div.outerHTML, or you can just append it anywhere you want.
To know more about elem(), visit element.js (GitHub).
I'm adding this answer not for the 8 year old question, but for the future visitors. Hope, it helps.
You can construct the html as a string in one variable like
var html = "";
html += "<a id='" + id1 +"'>link</a>";
html += "<div id='" + id1 +"'>div</div>";
// ... and so on
then you return the variable html
return html;
The better way would be to Import ElementsJS and just reference each element in it.
var root = document.getElementById("root");
var elementdiv = create_element('div',{'class':'divcss'}, root, null);
create_element('h1',{'class':'hellocss'}, elementdiv, "Hello World");
.hellocss {
color : red;
}
.divcss {
background-color : blue;
height: 100px;
position: absolute;
}
<script src="https://elementsjs.blob.core.windows.net/public/create-elements.js"></script>
<body id="root"></body>
For More Details Refer to https://github.com/divyamshu/Elements-JS
Well Documented with Example.
Here's simple illustration for converting the html-page (static), to javascript based html-page (dynamic).
Let us say, you have html-page as "index.html" (calling index_static.html here).
index_static.html
<!DOCTYPE HTML>
<html>
<head>
<title>Test</title>
</head>
<body>
<h1> Hello !!! </h1>
</body>
</html>
You can open this file in the browser, to see the desired output.
Now, lets create a javascript equivalent to this.
Use online-tool, to generate the javascript source (by pasting the above html file source to it). Therefore, it follows as:
dynamic.js
document.write("<!DOCTYPE HTML>");
document.write("<html>");
document.write(" <head>");
document.write(" <title>Test<\/title>");
document.write(" <\/head>");
document.write(" <body>");
document.write(" <h1> Hello !!! <\/h1>");
document.write(" <\/body>");
document.write("<\/html>");
And now, your dynamic version of the static_index.html will be as below:
index_dynamic.html
<script language="JavaScript" src="dynamic.js"></script>
Open the index_dynamic.html on the browser to validate the web-page (dynamic though, down-the-line).
more info

Nested custom element not rendered when parent element is instantiated programmatically

For a Single Page Application I have registered some custom elements to the customElementRegistry which are rendered from string literals via insertAdjacentHTML(). Nesting these elements is no problem when done in html. But when I try to instantiate a parent custom element programmatically starting from customElements.get('entry-page') or document.createElement('div', {is: 'entry-page'}), the nested custom elements do not render as if not defined at all. Even customElements.whenDefined('nested-element').then(...) doesn't help.
function registerCustomElement(name, tpl) {
class cls extends HTMLDivElement {
connectedCallback() {
this.insertAdjacentHTML('afterbegin', tpl)
}
};
customElements.define(name, cls, {
extends: 'div'
})
}
let tpl1 = '<p class="box">inner HTML</p>'
let tpl2 = '<article class="outer">outer HTML<inner-html></inner-html></article>'
let tpl3 = '<article class="outer">revised outer HTML<div is="inner-html"></div></article>'
registerCustomElement('inner-html', tpl1)
registerCustomElement('outer-html', tpl2)
registerCustomElement('outer-html-revised', tpl3)
document.querySelector('main')
.insertAdjacentHTML('beforeend', '<div is="outer-html"></div>')
document.querySelector('main')
.insertAdjacentHTML('beforeend', '<div is="outer-html-revised"></div>')
body {
font-family: 'sans serif';
font-size: 0.8em
}
.box {
padding: 1em;
color: white;
background-color: olive;
}
.outer {
padding: 0.5em;
margin-bottom: 0.5em;
border: 2px solid darkorange
}
<h1>Embedded in html</h1>
<h2>version with 'inner-html' element</h2>
<outer-html></outer-html>
<h2>version with 'div is inner-html' element</h2>
<outer-html-revised></outer-html-revised>
<h1>Embedded programmatically</h1>
<main></main>
Each element is defined separately as a ES6 Module. I am currently not using shadow DOM.
Why a custom element does fully render including all nested elements when embedded in HTML, but not when implemented in the DOM programmatically?
Edit As Danny Engelman stated, the only cross-browser option is to extend HTMLElement.
While in HTML the custom elements may be referenced by there names even in the string literals, the custom elements are based upon, this seems not to be true, when they are generated programmatically. In this case the spec requires something like <div is="custom-element-name">, but it wouldn't be cross-browser, as Apple only supports HTMLElement.

getComputedStyle() returns nothing, but getComputedStyle().getPropertyValue() returns as expected

I am trying to move an element from the light DOM to the shadow DOM, but when I do so the styling isn't copying over. I tried to fix this by setting the newElement.style = window.getComputedStyle(elem), but this hasn't seemed to work. The styles should be:
.card {
color: #ff0;
font-size: 3rem;
font-weight: 600;
border: 3px solid blueviolet;
background-color: greenyellow;
}
but the styles don't apply and when I print the getComputedStyle() to console what I see is:
all the values are empty
However, when I loop through the properties of getComputedStyle() with .getPropertyValue() like so:
for(let property of style){
console.log(`property: ${property}, value: ${style.getPropertyValue(property)}`);
}
what I get in the console is:
the correct values
So I'm confused as to why getComputedStyle() doesn't contain the values, but using getComputedStyle().getPropertyValue() returns the correct values. I'm sure I'm missing something obvious, as I couldn't find another post about this anywhere.
Any help would be greatly appreciated, thanks in advance.
EDIT: I've taken the code provided by Danny below and modified it to better show the issue I'm facing:
<style>
.card {
color: yellow;
background: green;
}
</style>
<my-element>
<div class="card">lightDOM reflected to shadowDOM</div>
</my-element>
<script>
customElements.define("my-element", class extends HTMLElement {
constructor(){
super().attachShadow({mode:"open"}).innerHTML = ``;
}
connectedCallback() {
setTimeout(() => { // wait till innerHTML is parsed
let card = this.children[0]; // Get the light DOM Card element
this.shadowRoot.appendChild(card.cloneNode(true)); // Append it to the shadowDOM
let style = window.getComputedStyle(card); // Get style of the Light DOM Card
this.shadowRoot.querySelector('.card').style = style; // Set the ShadowDOM card style equal to the Light DOM Style
console.log(style);
console.log(style.color); // yellow = rgb:255,255,0
console.log(style.background); // green = rgb:0,128,0
card.remove(); // Remove the card from the Light DOM to prevent duplication
})
}
})
</script>
Notice that the styling above doesn't apply even though it seems to be exactly as the docs specify:
"The returned object is the same CSSStyleDeclaration type as the object returned from the element's style property. However, the two objects have different purposes:
The object from getComputedStyle is read-only, and should be used to inspect the element's style — including those set by a element or an external stylesheet.
The element.style object should be used to set styles on that element, or inspect styles directly added to it from JavaScript manipulation or the global style attribute."
https://developer.mozilla.org/en-US/docs/Web/API/Window/getComputedStyle#description
From MDN Documentation:
The Window.getComputedStyle() method returns an object containing the values of all CSS properties of an element, after applying active stylesheets and resolving any basic computation those values may contain. Individual CSS property values are accessed through APIs provided by the object, or by indexing with CSS property names.
It's stated that you need to use API functions, such as getPropertyValue() to get the value of it.
Ref: https://developer.mozilla.org/en-US/docs/Web/API/Window/getComputedStyle
If you want to print all of the CSS styles from a specific element you may just iterate all the attributes like this:
function dumpCSSText(element){
var s = '';
var o = getComputedStyle(element);
for(var i = 0; i < o.length; i++){
s+=o[i] + ': ' + o.getPropertyValue(o[i])+';\n';
}
return s;
}
var e = document.querySelector('.card');
console.log(dumpCSSText(e));
.card {
color: #ff0;
font-size: 3rem;
font-weight: 600;
border: 3px solid blueviolet;
background-color: greenyellow;
}
<div class="card"></div>
property style is read-only so you can't assign anything to it;
(I stand corrected per comments; you can assign a value, but it
will override all values)
The innerHTML of Custom Elements is not parsed yet when the connectedCallback fires. So getting styles of its children with getComputedStyle is an operation on non-existing elements.
If you reflect the lightDOM contents to a <slot> in shadowDOM, there is no need to copy styles as the styling from lightDOM is reflected
<style>
.card {
color: yellow;
background: green;
}
</style>
<my-element>
<div class="card">lightDOM reflected to shadowDOM</div>
</my-element>
<script>
customElements.define("my-element", class extends HTMLElement {
constructor(){
super().attachShadow({mode:"open"}).innerHTML = `<slot></slot>`
}
connectedCallback() {
setTimeout(() => { // wait till innerHTML is parsed
let card = this.querySelector(".card"); // in lightDOM!
let style = window.getComputedStyle(card);
console.log(style.color); // yellow = rgb:255,255,0
console.log(style.background); // green = rgb:0,128,0
})
}
})
</script>
More reading:
https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/style
https://developer.mozilla.org/en-US/docs/Web/HTML/Element/slot
::slotted CSS selector for nested children in shadowDOM slot
wait for Element Upgrade in connectedCallback: FireFox and Chromium differences

Generate css dynamically via templates and placeholders

I want to generate css dynamically at run time.
Initially I had used sass and defined some variables and was using those variables. But css has to be generated first from the scss. Sass had given me flexibility to use variables and functions but still I was not able to changes them at run time via javascript.
One way was to change the inline styles via javascript but that approach was not completly flexible.
document.getElementById("myDiv").style.color = "red";
I don't want to do above, neither I want to attach any <style> attribute via javascript.
I want to use javascript but not for chaniging each and every style properties. I want to achieve scss like effect using css and javascript but at run time i.e dynamically.
E.g. suppose I got the color information from the ajax call now I want to change the whole theme of website based on that color received immediately without restarting or re-deploying my application.
e.g
as done in scss
.myClass {
background:$color;
// Update color value dynamically at run-time
}
Is it even possible or I am thinking in wrong direction!
Wound up playing with this and CSS variables. I'm adding a second answer because it's very different method from my first answer and it better aligns with your original question (updating CSS variables with JS).
BUT... don't do this. :) Browser support in IE < Edge doesn't exist and it is almost certainly slower than updating an on-page <style> element though I haven't tested it. This jsperf tests various style update methods. It doesn't include innerHTML on a single style element (likely the fastest) but you can see that the following CSS DOM methods are slower than the rest.
// get the stylesheet
// array position depends on how many style sheets you're loading.
// adjust as needed.
var sheet = document.styleSheets[0];
// simplest method: insertRule()
// setTimeout only for demo so you can see the change
window.setTimeout(function(){
// #media all {} is a trick to insert more than one
// selector and/or properties at once. Otherwise it's:
// sheet.insertRule(":root", "--header-color: green"); ...repeat...
sheet.insertRule("#media all { :root { --header-color: green; --main-color: orange; } }", 1);
}, 1200);
// SAFER method via addCSSRule.
// button and getAjaxStyles are just placeholders, obviously
var btn = document.querySelector('button');
btn.addEventListener("click", getAjaxStyles);
function getAjaxStyles() {
// success callback... break apart the json and update the CSS variables
addCSSRule(sheet, ":root", "--header-color: orange");
addCSSRule(sheet, ":root", "--main-color: blue");
addCSSRule(sheet, ":root", "--alt-color: red");
addCSSRule(sheet, ":root", "--borderColorA: lavender");
// or go with a single big string. definitely faster:
// addCSSRule(sheet, ":root", "--alt-color: red; --borderColorA: #0ff; ")
}
// Credit for addCSSRule() goes to Diego Flórez in a comment on
// https://davidwalsh.name/add-rules-stylesheets
var addCSSRule = function(sheet, selector, rules) {
//Backward searching of the selector matching cssRules
var index = sheet.cssRules.length - 1;
for (var i = index; i > 0; i--) {
var current_style = sheet.cssRules[i];
if (current_style.selectorText === selector) {
//Append the new rules to the current content of the cssRule;
rules = current_style.style.cssText + rules;
sheet.deleteRule(i);
index = i;
}
}
if (sheet.insertRule) {
sheet.insertRule(selector + "{" + rules + "}", index);
} else {
sheet.addRule(selector, rules, index);
}
return sheet.cssRules[index].cssText;
}
/* Set initial CSS variables */
:root {
--header-color: #333;
--main-color: #888;
--alt-color: #bbb;
--borderColorA: #ccc;
}
h1 {
color: var(--header-color);
}
p {
border-bottom: 1px solid var(--borderColorA);
color: var(--main-color);
}
p+p {
color: var(--alt-color);
}
<h1>header</h1>
<p>paragraph 1</p>
<p>paragraph 2</p>
<button>Update CSS Variables</button>
To expand on the information that is provided in the linked "possible duplicate" question, you could easily set up a "default" set of styles in your page CSS file and then create a inline <style> ekement containing any overrides based on the response from your AJAX call. As long as the element/class/id definitions are the same in the two locations (i.e., CSS file and inline style section), specificity will cause the inline definitions to override the CSS ones.
So, using your example, your static CSS file would contain:
.myClass {
background: #FFFFFF;
}
. . . so that there is a default value if the AJAX call were to fail, and then your dynamically created <style> section would contain:
.myClass {
background: THE_AJAX_RESPONSE_VALUE;
}
. . . which would override the default value.
UPDATE #1:
Based on your sample JSON, this would be REALLY easy . . . you would loop through each top-level property of the JSON and create this:
KEY_NAME {
. . .
}
Then, within that block, loop through each property within that property and add the keys and values to create the style definitions:
KEY_NAME {
key1: value1,
key2: value2,
. . .
keyN: valueN
}
UPDATE #2:
You can also use StyleSheet and CSSStyleSheet interfaces to access the rules that are in the existing stylesheets, but, given that it uses an array-like structure, that means looping through all of the CSS definitions to find the one that you want and alter it. An example of how to do that can be found in this answer to another SO question: Is it possible to alter a CSS stylesheet using JavaScript? (NOT the style of an object, but the stylesheet itself)
Between the two approaches, though, creating an overriding <style> section seems like the easier approach.
Since the JSON has both the element names and the related styles, refreshing an on page stylesheet (vs inline element styles) would probably be the fastest since it uses innerHTML and only requires a single DOM lookup.
You'll need to loop through your JSON to create CSS compatible strings and then just dump it into the onpage style element. You can append CSS by concatenating the existing innerHTML with the new CSS string. I added an ID to the stylesheet for simplicity but you could also generate the style element when needed.
var StringifiedAjaxStyleObject = "h1 {background-color: #ecc; color: #633}";
var styleSheet = document.getElementById("style-update");
// some additional fake test style returns...
var testStyle1 = "h1 {background-color: #ccc; color: #333}";
var testStyle2 = "h1 {background-color: #667; color: #bbc}";
var testStyle3 = "h1 {background-color: #fee; color: #b00}";
// some fake ajax returns...
window.setTimeout(function() {
styleSheet.innerHTML = StringifiedAjaxStyleObject;
}, 1000);
window.setTimeout(function() {
styleSheet.innerHTML = testStyle1;
}, 2000);
window.setTimeout(function() {
styleSheet.innerHTML = testStyle2;
}, 3000);
window.setTimeout(function() {
styleSheet.innerHTML = testStyle3;
}, 4000);
/* base styles ... */
h1 {
padding: 5px;
background-color: #eef;
color: #007
}
<!-- empty stylesheet -->
<style id="style-update" type="text/css" rel="stylesheet"></style>
<h1>Hi, mom</h1>
<button>Update Styles<button>
EDIT:
Here's a slightly more real-world version based on the JSON object in your comment. Trigger it via the button.
var styleSheet = document.getElementById("style-update");
var btn = document.querySelector('button');
btn.addEventListener("click", updateStyles);
function updateStyles() {
var StringifiedAjaxStyleObject
, newCSS
, ajaxReturn
;
// ...your ajax method to get the new styles...
// on success...
ajaxReturn = {
".base": {
"background-color": "#b83605",
"border-color": "#543927",
"color": "gray",
"text-shadow": "0 -1px 0 rgba(0, 0, 0, 0.15)"
},
".overlay": {
"background": "rgba(76, 65, 80, 0.2)",
"color" : "#ddd"
}
};
// Convert the object to a string
newCSS = cssStringFromJson(ajaxReturn);
// Update the stylesheet
styleSheet.innerHTML = newCSS;
}
function cssStringFromJson(cssJSON) {
var styleStr = "",
i, j;
for (i in cssJSON) {
styleStr += i + " {\n"
for (j in cssJSON[i]) {
styleStr += "\t" + j + ": " + cssJSON[i][j] + ";\n"
}
styleStr += "}\n"
}
return styleStr;
}
/* base styles ... */
.base {
border: 1px solid #ccf;
background-color: #eef;
color: #000;
padding: 15px;
}
.overlay {
padding: 5px 15px;
background: rgba(96, 95, 180, 0.2);
}
body {
font-family: sans-serif;
}
button {
margin-top: 1em;
font-size: 1em;
}
<!-- empty stylesheet -->
<style id="style-update" type="text/css" rel="stylesheet"></style>
<div class="base">
<p>.base</p>
<div class="overlay">
<p>.overlay</p>
</div>
</div>
<button>Update Styles</button>
You can try angular templates.
It is going to break your previous sass, but it will work out later.

Dynamically creating HTML elements using Javascript?

I want to dynamically create some HTML elements (3 html element) and then return this html code as a string in a variable. I don't want to write the HTML code in the following function to some div, but, I want to return it in a var.
function createMyElements(id1,id2,id3){
//create anchor with id1
//create div with id 2
//create xyz with id3
//now return the html code of above created just now
}
How can I do this?
[Edit 2021/10] This answer is now > 10 years old. Here is a snippet containing several ways to create and/or inject elements. The answer for the question asked (create some element(s) and retrieve their html code) can be found # the bottom of the snippet.
// The classic createElement
// -------------------------
// create a paragraph element using document.createElement
const elem = document.createElement(`p`);
elem.id = `myBrandnewDiv1`;
// put in some text
elem.appendChild(document.createTextNode(`My brand new div #1`));
// append some html (for demo, preferrably don't use innerHTML)
elem.innerHTML += ` => created using
<code>document.createElement</code>`;
// append a new paragraph within #myBrandNewDiv1
const nested = elem.appendChild(document.createElement(`p`));
nested.classList.add(`nested`);
// add some text to that
nested.textContent = `I am nested!`;
// the elements are still in memory, now add the
// whole enchillada to the document
document.body.appendChild(elem);
// insertAdjacentHTML
// ------------------
// nest an element within the nested div
nested.insertAdjacentHTML(`afterbegin`,
`<div id="nestedWithin#nested">
This text will appear <i>above</i> the text of
my parent, that being div#nested.
Someone had the nerve to insert me using
<code>insertAdjacentHTML</code>
</div>`);
// Object.assign
// -------------
// Use Object.assign to create an element and
// assign properties/html to it in one go
const newElem = Object.assign(
document.createElement(`div`),
{ id: `myBrandnewDiv2`,
innerHTML: `div#myBrandnewDiv2 signing in.
I was <i>assigned</i> using <code>Object.assign</code>…`});
document.body.appendChild(newElem);
// insertAdjacentElement combined with Object.assign
// -------------------------------------------------
// use the above technique combined with insertAdjacentElement
newElem.insertAdjacentElement(
`beforeend`,
Object.assign(document.createElement(`span`),
{ id: `myBrandnewnested2_nested`,
innerHTML: `<br>Me too! And appended I was
with <code>insertAdjacentElement</code>` })
);
// createDocumentFragment
// ----------------------
// Use a document fragment to create/inject html
const fragment = document.createDocumentFragment();
const mdnLnk = `https://developer.mozilla.org/en-US/` +
`docs/Web/API/Document/createDocumentFragment`;
fragment.appendChild(
Object.assign(
document.createElement(`p`),
{innerHTML: `Regards from <code>createDocumentFragment</code>
(see MDN)`})
);
document.querySelector(`#myBrandnewDiv2`).appendChild(fragment);
// Create, but don't inject
// ------------------------
const virtual = Object.assign(
document.createElement(`p`),
{ innerHTML: `
id1
<div id="id2">Hi!</div>
<p id="id3">Hi 2!</p>`,
classList: [`xyz`], } );
const prepareHtml4Reporting = html =>
html.replace(/</g, `<`)
.replace(/\n\s+/g, `\n`)
.replace(/\n\n/g, `\n`);
document.body.insertAdjacentHTML(
`beforeend`,
`<h3>html only</h3><pre>${
prepareHtml4Reporting(virtual.innerHTML)}</pre>`);
body {
font: normal 12px/15px verdana, arial, sans-serif;
margin: 2rem;
}
code {
background-color: #eee;
}
.nested {
margin-left: 0.7rem;
max-width: 450px;
padding: 5px;
border: 1px solid #ccc;
}
I have used some of these methods in this library (see /src/DOM.js), with a mechanism for sanitizing html before it is injecting.
Html:
<div id="main"></div>
JavaScript:
var tree = document.createDocumentFragment();
var link = document.createElement("a");
link.setAttribute("id", "id1");
link.setAttribute("href", "http://site.com");
link.appendChild(document.createTextNode("linkText"));
var div = document.createElement("div");
div.setAttribute("id", "id2");
div.appendChild(document.createTextNode("divText"));
tree.appendChild(link);
tree.appendChild(div);
document.getElementById("main").appendChild(tree);
The main reason to use a documentFragment in stead of just adding the elements directly is speed of execution.
At this size it doesn't matter, but when you start adding hundreds of elements, you will appreciate doing it in-memory first :-)
With documentFragment you can construct a whole tree of DOM-elements in-memory and will not afffect the browser DOM untill the last moment.
Otherwise it forces the browser to update for every element, which sometimes can be a real pain to watch.
If you're doing this repeatedly (dynamically creating HTML), you might want to use a more general approach.
If you want to create three unrelated elements, you can do:
var anchor = elem("a", {"id":"id1"});
var div = elem("div", {"id":"id2"});
var xyz = elem("div", {"id":"id3"});
Now, you have three elements. If you want to get the HTML of these (as string), simply do:
var html = anchor.outerHTML + div.outerHTML + xyz.outerHTML;
If you want to have these three in an element (say, div), do:
var div = elem("div", null, [
elem("a", {"id":"id1"}),
elem("div", {"id":"id2"}),
elem("div", {"id":"id3"}),
]);
You can get the HTML with div.outerHTML, or you can just append it anywhere you want.
To know more about elem(), visit element.js (GitHub).
I'm adding this answer not for the 8 year old question, but for the future visitors. Hope, it helps.
You can construct the html as a string in one variable like
var html = "";
html += "<a id='" + id1 +"'>link</a>";
html += "<div id='" + id1 +"'>div</div>";
// ... and so on
then you return the variable html
return html;
The better way would be to Import ElementsJS and just reference each element in it.
var root = document.getElementById("root");
var elementdiv = create_element('div',{'class':'divcss'}, root, null);
create_element('h1',{'class':'hellocss'}, elementdiv, "Hello World");
.hellocss {
color : red;
}
.divcss {
background-color : blue;
height: 100px;
position: absolute;
}
<script src="https://elementsjs.blob.core.windows.net/public/create-elements.js"></script>
<body id="root"></body>
For More Details Refer to https://github.com/divyamshu/Elements-JS
Well Documented with Example.
Here's simple illustration for converting the html-page (static), to javascript based html-page (dynamic).
Let us say, you have html-page as "index.html" (calling index_static.html here).
index_static.html
<!DOCTYPE HTML>
<html>
<head>
<title>Test</title>
</head>
<body>
<h1> Hello !!! </h1>
</body>
</html>
You can open this file in the browser, to see the desired output.
Now, lets create a javascript equivalent to this.
Use online-tool, to generate the javascript source (by pasting the above html file source to it). Therefore, it follows as:
dynamic.js
document.write("<!DOCTYPE HTML>");
document.write("<html>");
document.write(" <head>");
document.write(" <title>Test<\/title>");
document.write(" <\/head>");
document.write(" <body>");
document.write(" <h1> Hello !!! <\/h1>");
document.write(" <\/body>");
document.write("<\/html>");
And now, your dynamic version of the static_index.html will be as below:
index_dynamic.html
<script language="JavaScript" src="dynamic.js"></script>
Open the index_dynamic.html on the browser to validate the web-page (dynamic though, down-the-line).
more info

Categories