I've been working on an app that fetches data from an API and then neatly puts them into card div's. I've written the code for performing the request and getting all the data in JSON (image below), however I can't find a way to keep my code clean and manage the results.
What i want to do is create a div called card for each JSON object (there are 50 in the picture below) and then inside those divs i append span tags with the information.
Here's my current code
xhr.onreadystatechange = () => {
if (xhr.readyState == 4) {
results.style.opacity = 1
let result = xhr.responseText
result = JSON.parse(result)
console.log(result)
Create the function and pass the JSON data to that function and then you need to iterate the loop for the key name results. Then access the each element by using the key name of the array's object. Below is the example code (css not included). More about object
<body>
<div id="container">
</div>
</body>
<script>
xhr.onreadystatechange = () => {
if (xhr.readyState == 4) {
results.style.opacity = 1
let result = xhr.responseText
result = JSON.parse(result)
loadDiv(result);
}
}
function loadDiv(data){
for(var x of data.results){
var div = `<div class="cols">
${x.mal_id}
<img src="${x.url}"/>
</div>
`;
document.getElementById("container").insertAdjacentHTML("beforeend",div);
}
}
You can iterate the object and create divs and spans.
// I expect your results in this variable.
var result = {
results: [{
One: 'div',
Two: 'span'
}]
};
result.results.forEach(data => {
var div = document.createElement('div');
div.id = 'block';
div.className = 'block';
var span = document.createElement('span');
span.className = 'block-2';
div.appendChild(span);
document.getElementsByTagName('body')[0].appendChild(div);
});
.block {
height: 50px;
width: 50px;
border: 1px solid red;
}
.block-2 {
height: 20px;
width: 20px;
border: 1px solid blue;
position: absolute;
}
Related
The styles are preserved through style inlining if I select and copy a nicely styled HTML table (or any other element) in a page and then paste it inside a new email message in Gmail or Outlook composing interface.
Example:
I would like to trigger via Javascript this style-inlined HTML that Chrome generates for pasting, to generate an HTML to be used for generating a formatted XLS or an email message.
Notes:
Version 3* due to misreading question
Copying to clipboard does not work in StackOverflow snippet, try offline
Original answer, using getComputedStyle()
We can use getComputedStyle() to get all the computed styling for an element.
We can map this to inline css and add it to the DOM using setAttribute
Since getComputedStyle() literally returns all the styling, most of them won't be needed. I'd recommend using a whitelist style. (See second snippet below)
Small demo, press the button to copy the text.
All the div style's will be inline, somewhere in that huge list will be border etc.
function getOuterHTMLWithInlineStyle(el) {
let s = getComputedStyle(el);
let i = []
for (let key in s) {
if (!Number.isInteger(+key)) {
let prop = key.replace(/\-([a-z])/g, v => v[1].toUpperCase());
i.push(`${key}: ${s[key]}`);
}
}
el.setAttribute('style', i.join("; "));
return el.outerHTML;
}
function copy() {
const e = document.querySelector('div')
const html = getOuterHTMLWithInlineStyle(e)
navigator.clipboard.writeText(html).then(() => {
console.log('Succesfully copied HTML to clipboard');
}, function(err) {
console.error('Could not copy text.\nTried to copy the following:\n', html);
});
}
div {
padding: 5px 25px;
border: 1px solid red;
}
<div>Hello World!</div>
<br>
<button onClick='copy()'>Click me to copy</button>
Second answer, using a whitelist
Same demo as above, but with the whitelist added:
Will copy the following HTML:
<div style="border: 1px solid rgb(255, 0, 0); padding: 5px 25px">Hello World!</div>
function getOuterHTMLWithInlineStyle(el, whitelist = [ 'border', 'padding' ]) {
let s = getComputedStyle(el);
let i = []
for (let key in s) {
if (whitelist.includes(key)) {
let prop = key.replace(/\-([a-z])/g, v => v[1].toUpperCase());
i.push(`${key}: ${s[key]}`);
}
}
el.setAttribute('style', i.join("; "));
return el.outerHTML;
}
function copy() {
const e = document.querySelector('div')
const html = getOuterHTMLWithInlineStyle(e)
navigator.clipboard.writeText(html).then(() => {
console.log('Succesfully copied HTML to clipboard');
}, function(err) {
console.error('Could not copy text.\nTried to copy the following:\n', html);
});
}
div {
padding: 5px 25px;
border: 1px solid red;
}
<div>Hello World!</div>
<br>
<button onClick='copy()'>Click me to copy</button>
Third answer, using document.styleSheets
Oke, v3, based on this SO answer where the user uses document.styleSheets to get only the css from stylesheets and filter that on those who are non-default.
Using a map() and regex, we can get a string with just containing the CSS values you need.
Note this might not work 100%, the regex does not take into account that CSS values might contain {'s. You should improve the regex if you have very complex values.
The (collapsed) snippet below will output/copy the following HTML:
<div style="padding: 5px 25px; border: 1px solid red;">Hello World!</div>
var proto = Element.prototype;
var slice = Function.call.bind(Array.prototype.slice);
var matches = Function.call.bind(proto.matchesSelector ||
proto.mozMatchesSelector || proto.webkitMatchesSelector ||
proto.msMatchesSelector || proto.oMatchesSelector);
var elementMatchCSSRule = function(element, cssRule) {
return matches(element, cssRule.selectorText);
};
var cssRules = slice(document.styleSheets).reduce(function(rules, styleSheet) {
return rules.concat(slice(styleSheet.cssRules));
}, []);
var getAppliedCss = function(elm) {
var elementRules = cssRules.filter(elementMatchCSSRule.bind(null, elm));
var rules =[];
if (elementRules.length) {
for (i = 0; i < elementRules.length; i++) {
rules.push(elementRules[i].cssText)
}
}
return rules.map(r => /\{(.+)\}/g.exec(r)[1].trim()).join("; ");
}
function getOuterHTMLWithInlineStyle(el) {
el.setAttribute('style', getAppliedCss(el));
return el.outerHTML;
}
function copy() {
const e = document.querySelector('div')
const html = getOuterHTMLWithInlineStyle(e)
navigator.clipboard.writeText(html).then(() => {
console.log('Succesfully copied HTML to clipboard');
}, function(err) {
console.error('Could not copy text.\nTried to copy the following:\n', html);
});
}
div {
padding: 5px 25px;
border: 1px solid red;
}
<div>Hello World!</div>
<br>
<button onClick='copy()'>Click me to copy</button>
For a project, I am building an app that gets user input, stores it in an array, and displays the input in the DOM. I did the first two parts but I am having trouble displaying it. More specifically, I can't get the CSS to show up.
I have tried .createElement() which creates a new list-item but it does not include CSS. I am starting to think I am completely going about this incorrectly. If you need more information or code let me know.
\\HTML
<div id="boxhold">
<ul>
<li>
<div class="twogrid">
<h1>Fruit Juice</h1>
<p>50</p>
</div>
</li>
</ul>
</div>
\\CSS
#boxhold {
margin: 0 auto;
ul {
list-style-type: none;
padding: 0;
li {
margin: 0 auto;
width: 408px;
height: 75px;
border: 3px solid $prime-color;
h1 {
font-family: $header-font;
font-weight: $header-weight;
font-size: 1em;
}
p {
font-family: $header-font;
font-weight: $header-weight;
}
}
}
}
\\JS
//Get Data
//Empty array for storing
var added = [];
//Get Data
var userInput = function() {
return {
name: document.getElementById('name').value,
amount: document.getElementById('amount').value
}
};
// Store Data
var newSugar = function(){
return added.push(userInput());
}
// New HTML
function newBox() {
var newLi = document.createElement('li');
var newName = document.getElementById('name').value;
var n = document.createTextNode(newName);
newLi.appendChild(n);
var newAmount = document.getElementById('amount').value;
var a = document.createTextNode(newAmount);
newLi.appendChild(a);
var boxhold = document.getElementById('boxhold').getElementsByTagName('ul')[0];
document.body.appendChild(newLi);
};
//Adding stuff
var displayData = (function() {
var addInput = function() {
var data = userInput();
var item = newSugar();
var box = newBox();
//var box = newItem();
};
var addFood = document.getElementById('addFood');
addFood.addEventListener('click', addInput);
document.addEventListener('keypress', function(event) {
if (event.keyCode === 13 || event.which === 13) {
addInput();
}
});
})(userInput, newSugar, newBox);
Welcome to Stack Overflow #nzart 👋
It looks like you're appending the newly created list item to the document's body, which means it will be added as the last element of the page. Your CSS indicates that the styles only apply to list items inside of an unordered list, so this would explain the lack of styles.
The simple fix should be to replace document.body.appendChild(newLi); with boxhold.appendChild(newLi);. I hope this helps!
I'm trying to set up a service in python using pdfKit to create a pdf file from html files.
So basically I will send my element as string and expect the server to return a pdf version of it, but to be an accurate representation I also need to send a css file of the element.
How can I do this? Generate JSON / object with only the relevant style properties and selectors of an element and all its children. Respecting hierarchy and no duplicates. There are similar questions but they are outdated and tend to not consider children elements.
I was thinking maybe there is a way to create a new DOM from this element and then get the root css?
Here is something I came up with, basically pass the element you want to extract the styles of and ones of its children, and it will return you the stylesheet as a string. Open your console before running the snippet and you will see the output from the console.log.
Because I wanted to support the extraction of every element even those without a selector, I had to replace each element id by a unique uuid specifically generated for them in order to facilitate the styling of your output. The problem with this approach is in case you are using ids for styling or for user interaction, you are going to loose such functionality on concerned elements after calling extractCSS.
However, it is pretty trivial to use the oldId I'm passing to change back once your pdfKit process finished the generation. Simply call swapBackIds passing the elements returned by the function. You can see the difference of behavior if you uncomment the call in my snippet: the #root pink background would disappear because the styling targets an element id.
All in all, you need to:
Call extractCSS with the element you want to extract
Generate your pdf using res.stylesheet
Call swapBackIds with res.elements
// Generate an unique id for your element
// From https://stackoverflow.com/a/2117523/2054072
function uuidv4 () {
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
var r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8);
return v.toString(16);
});
}
// Flatten an array
// https://stackoverflow.com/a/15030117/2054072
function flatten(arr) {
return arr.reduce(function (flat, toFlatten) {
return flat.concat(Array.isArray(toFlatten) ? flatten(toFlatten) : toFlatten);
}, []);
}
function recursiveExtract (element) {
var id = uuidv4()
var oldId = element.id
var computed = window.getComputedStyle(element)
var style = computed.cssText
// Now that we get the style, we can swap the id
element.setAttribute('id', id)
// The children are not a real array but a NodeList, we need to convert them
// so we can map over them easily
var children = Array.prototype.slice.call(element.children)
return [{ id: id, style: style, oldId: oldId }].concat(children.map(recursiveExtract))
}
function extractCSS (element) {
if (!element) { return { elements: [], stylesheet: '' } }
var raw = recursiveExtract(element)
var flat = flatten(raw)
return {
elements: flat,
stylesheet: flat.reduce(function (acc, cur) {
var style = '#' + cur.id + ' {\n' + cur.style + '\n}\n\n'
return acc + style
}, '')
}
}
var pdfElement = document.querySelector('#root')
var res = extractCSS(pdfElement)
console.log(res.stylesheet)
function swapBackIds (elements) {
elements.forEach(function (e) {
var element = document.getElementById(e.id)
element.setAttribute('id', e.oldId)
})
}
swapBackIds(res.elements)
#root {
background-color: pink;
}
.style-from-class {
background-color: red;
width: 200px;
height: 200px;
}
.style-from-id {
background-color: green;
width: 100px;
height: 100px;
}
<div id="root">
<span>normal</span>
<span style="background: blue">inline</span>
<div class="style-from-class">
style-class
</div>
<div class="style-from-id">
style-id
<div style="font-size: 10px">a very nested</div>
<div style="font-size: 12px; color: white">and another</div>
</div>
</div>
<div id="ignored-sibling">
</div>
let para = document.querySelector('p');
let compStyles = window.getComputedStyle(para);
para.textContent = 'My computed font-size is ' + compStyles.getPropertyValue('font-size') + ',\nand my computed background is ' + compStyles.getPropertyValue('background') + '.';
p {
width: 400px;
margin: 0 auto;
padding: 20px;
font: 2rem/2 sans-serif;
text-align: center;
background: purple;
color: white;
}
<p>Hello</p>
you can use getComputedStyle method to get computed value of style property
Currently the width() method and all of it's variations in jQuery return pixel values. The same happens when calling the css('width') method.
I have elements, which are styled in .css files, and I have no way of knowing if they're styled in percentages or pixels, but in case it's in percentage or no width is explicitly set on the element, I need to get a percent value.
For example if I have the following:
.seventy { width: 70%; }
.pixels { width: 350px; }
<div class="seventy"></div>
<div class="pixels"></div>
<div class="regular"></div>
I would need these results.
$('.seventy').method() //=> '70%'
$('.pixels').method() //=> '350px'
$('.regular').method() //=> '100%' since that's how block elements behave
Is there anything in jQuery I can use to achieve this effect? Or a custom approach to it?
You can parse the document.stylesheets to find a match. Note, this will only return the actual style after the browser has parsed it so is of no use for getting raw unadulterated CSS as written in file. For that you'd need to parse the file itself rather than the document.stylesheets.
This code is old and untested so your mileage may vary. I have no idea how well it performs with inherited values or more complicated selectors.
//TEST PARSE CSS
var CSS = function () {
var _sheet;
var _rules;
function CSS() {
_sheet = document.styleSheets[0];
if (_sheet.rules) {
_rules = _sheet.rules; // IE
} else {
_rules = _sheet.cssRules; // Standards
}
this.find = function (selector) {
var i = _rules.length;
while(i--){
if (_rules[i].selectorText == selector) {
break;
}
if(i==0){break;}
}
//return _rules[i].cssText;
return _rules[i].style;
}
this.set = function (foo) {
//to do
}
};
return new CSS();
};
//init
var css = new CSS();
//view the console.
console.log(css.find(".regular"));//Note how the width property is blank
//update elements with the results
document.querySelector(".seventy").innerHTML = css.find(".seventy").width;
document.querySelector(".pixels").innerHTML = css.find(".pixels").width;
document.querySelector(".regular").innerHTML = css.find(".regular").width;
//other tests
document.getElementById("a").innerHTML = css.find("body").color;
document.getElementById("b").innerHTML = css.find("h1").color;
document.getElementById("c").innerHTML = css.find("h1").width;
document.getElementById("d").innerHTML = css.find(".notInDom").color;
body {
font-family:sans-serif;
color:black;
background-color:#cccccc;
}
h1 {
color:blue;
font-size:1.5em;
font-weight:400;
width:70%;
}
.seventy, .pixels, .regular {display:block; border:1px solid red;}
.seventy {display:block; border:1px solid red; width: 70%; }
.pixels { width: 350px; }
.regular {}
.notInDom {
color:red;
}
<h1>Find and Read Style Attributes Directly from the Stylesheet.</h1>
<div class="seventy"></div>
<div class="pixels"></div>
<div class="regular"></div>
<ul>
<li>css.find("body").color = <span id='a'></span></li>
<li>css.find("h1").color = <span id='b'></span></li>
<li>css.find("h1").width = <span id='c'></span></li>
<li>css.find(".notInDom").color = <span id='d'></span></li>
</ul>
<p>This is a work in progress and hasn't been tested in any meaningful way. Its messy and very limited.</p>
function getStyle(className) {
var classes = document.styleSheets[0].rules || document.styleSheets[0].cssRules;
for (var x = 0; x < classes.length; x++) {
if (classes[x].selectorText == className) {
(classes[x].cssText) ? alert(classes[x].cssText) : alert(classes[x].style.cssText);
}
}
}
getStyle('.test');
I want to insert a node, that is converted to a string, on a specific position.
the problem is that i want to keep or regain the instance of the node, but dont know how.
I hope some of you have a solution
Note: I have to insert the element as string!
HTML:
<div class="div">
Here are <span class="span"></span> some text
</div>
CSS:
.div, .span {
display: inline-block;
border: 1px solid #333333;
width: 200px;
height: 100px;
}
.span {
min-width: 10px;
max-width: 50px;
height: 50px;
}
JS:
var div = document.querySelector('.div');
var span = document.querySelector('.span');
// element to insert
var newElement = document.createElement('div');
newElement.innerHTML = 'Hallo!';
// convert to string
var converter = document.createElement('div');
converter.appendChild(newElement);
// insert element at the end of span
span.innerHTML = span.innerHTML + converter.innerHTML;
// get the instance of the node? to interact with it
newElement.style.color = '#00ff00';
Example: CODEPEN
I wrote small function for test:
function makeNode(innerHTML){
var node = document.createElement('div');
node.id = 'unique_id';
node.innerHTML = innerHTML;
document.body.innerHTML += node.outerHTML;
return document.getElementById('unique_id');
}
Create element, insert in body, and return it
var elm = makeNode('test div');
Apply some style
elm.style = 'color:red;';
Try it: https://jsfiddle.net/nv6gyLve/
You need to set styles before insert element to dom. Other way -getElementById or same functions