Endless Variables
Often I find myself assigning numerous DOM elements to a list variables, and naming them gets increasingly difficult as things grow more complex.
const header = document.querySelector('header')
const headerWrapper = header.querySelector('.header-wrapper')
const headerH1 = headerWrapper.querySelector('h1')
const headerH2 = headerWrapper.querySelector('h2')
// etc...
Object Notation
It occurred to me try referencing the DOM hierarchy using Objects instead and came up with something like this:
// assign DOM elements to object properties
const homePage = {
headerContainer: document.querySelector('header'),
headerWrapper: document.querySelector('header>.header-wrapper'),
header: {
h1: document.querySelector('header>.header-wrapper>h1'),
h2: document.querySelector('header>.header-wrapper>h2'),
}
}
// update element content
homePage.header.h1.innerText = "New Header Text"
This method is flawed however:
Parent nodes must be given unique keys to reference directly
Objects can't refer to themselves until they have been initialized, so I can't have h1: this.headerContainer.querySelector('h1')
Instead of relying on headerContainer it would be better to have homePage.header, as well as the h1 tag within that container with homePage.header.h1 but I'm not sure if that's possible with JavaScript/JSON.... to my mind it would be something like this:
// pseudocode
const homePage = {
header:
document.querySelector('header'),
{
h1: this.header.querySelector('h1'),
h2: this.header.querySelector('h2'),
}
}
// update element content
homePage.header.h1.innerText = "Cleaner Syntax"
Is there some other means of doing this? Using Classes or Object Constructors perhaps?
Edit 1:
Solution: Using Classes
I think what I want to work out is how to store elements as an attribute of another element or object property, and I think I have worked out how to do that using Classes:
class Page {
constructor(body = document.querySelector('body')) {
this.header = body.querySelector('header')
this.header.h1 = this.header.querySelector('h1')
}
}
const homePage = new Page()
homePage.header.style.backgroundColor='yellow'
homePage.header.h1.textContent = "Hello World"
Related
What I am trying to do is with an input add words into an array, then as you add the words I want them to be displayed in a list. So I did a function to render the list, and used a for each, then used that function inside the function that push the words into the array. The thing is that when you add the words the for each executes and duplicates all the words.
if you add tree as a word you get the output: tree
if you add rabbit you get the output : tree, tree, rabbit
lets say you want to add falcon and you get: tree, tree, rabbit, tree, tree, rabbit, falcon
Here is the code
const renderPass = function (array, location) {
array.forEach((element) => {
let html = `<li class="k">${element}</li>`;
location.insertAdjacentHTML("afterbegin", html);
});
};
const savingKeyWords = function (e) {
e.preventDefault();
keyWords.push(keyWord.value);
renderPass(keyWords, listOfKeys);
console.log(keyWords);
clear(keyWord);
};
could you help me with this??
That is because you seem to be using the same array defined in the global context over and over on every invocation.
Just create a new array when ever saving keywords is invoked.
const savingKeyWords = function (e) {
let keyWords = [];
e.preventDefault();
keyWords.push(keyWord.value);
renderPass(keyWords, listOfKeys);
console.log(keyWords);
clear(keyWord);
};
I think 'keyWords' has to be replaced with 'keyWord.value' in here:
renderPass(keyWords, listOfKeys);
And
const renderPass = function (keyWord, location) {
let html = `<li class="k">${keyWord}</li>`;
location.insertAdjacentHTML("afterbegin", html);
};
I hope this will be helpful for you. Thanks
Is there a simple way in which I can pass a "large" object of element attributes via an object?
For example:
const myAttrs = {
one: "one",
two: "two",
...,
fifty: "fifty"
};
return(
<MyElement data={myAttrs}>Some label</MyElement>
);
Such that I can access and set all of these values in the defined element itself...
const MyElement = styled.a.attrs(props => ({
// I want to break down and set each object prop here, rather than
// do it all individually like...
//
// one: props.one,
// two: props.two,
// ...
}))`
// CSS here...
`;
Use spread operator
<MyElement {...myAttrs}>Some label</MyElement>
const MyElement = styled.a.attrs({one, two, three} => ({
// you can access these properties directly by using one for props.one and so on
Pass large number of props from parents to child
<Content
accounts={this.props.accounts}
showBalance={this.props.showBalance}
withdraw={this.props.withdraw}
deposit={this.props.deposit}
current={this.props.current}
depositAmount={this.props.depositAmount}
handleDeposit={this.props.handleDeposit}
handleRm={this.props.handleRm}
amounts={this.props.amounts}
getWithdraw={this.props.getWithdraw}
/>;
could be replaced with
<Content {...this.props} />
You can destructed in child component
const {accounts,showBalance,withdraw,.....,getWithdraw} = this.props;
On the back of the spread work done by #kooskoos, I found a way in which I think I can include all of the attributes I like.
Basically we include the spreaded object in the HTML
<MyElement {...myAttrs}>Label</MyElement>
then in the const we can access these props easily by (instead of individually highlighting the attributes), we can simply write as such:
export const MyElement = styled.a.attrs(props => props)`...`
and this will pass through ALL props defined in the object, and attach them to the HTML element as you have defined them.
<MyElement one="one" two="two" three="three" ...>Label</MyElement>
I have a set of destructured variables that all have the same parent class. Is there a way to remove 'hollow-gallery-01' from each and add that to the destructuring process?
const [modal,ctrls,slides,close,images,arrows] =
[
document.querySelector('.hollow-gallery-01 .modal'),
document.querySelectorAll('.hollow-gallery-01 .control'),
document.querySelectorAll('.hollow-gallery-01 .slide'),
document.querySelector('.hollow-gallery-01 .close'),
document.querySelectorAll('.hollow-gallery-01 .img-wrap'),
document.querySelectorAll('.hollow-gallery-01 .arrow')
]
You could do
const [[modal], ctrls, slides, [close], images, arrows] = ["modal", "control", "slide", "close", "img-wrap", "arrow"].map(sel =>
document.querySelectorAll(".hollow-gallery-01 ." + sel)
);
to reduce the repetition.
That said, a better and more efficent approach (as also suggested by the other answers) is to use the selectors on the parent element, not document:
const gallery = document.querySelector('.hollow-gallery-01');
const modal = gallery.querySelector('.modal'),
ctrls = gallery.querySelectorAll('.control'),
slides = gallery.querySelectorAll('.slide'),
close = gallery.querySelector('.close'),
images = gallery.querySelectorAll('img-wrap'),
arrows = gallery.querySelectorAll('arrow');
This will work for you. console.log and html is for demo purposes.
var parentobj = document.getElementsByClassName("hollow-gallery-01")[0];
const [modal,ctrls,slides,close,images,arrows] =
[
parentobj.querySelector('.modal'),
parentobj.querySelectorAll('.control'),
parentobj.querySelectorAll('.slide'),
parentobj.querySelector('.close'),
parentobj.querySelectorAll('.img-wrap'),
parentobj.querySelectorAll('.arrow')
];
console.log(modal.innerHTML);
<div class="hollow-gallery-01">
<div class="modal">test</div>
</div>
It's not quite clear why you're using destructuring here rather than assigning the variables directly, but if you wanted to simplify the selectors you could get the parent class, and then use querySelector on that.
const parent = document.querySelector('.hollow-gallery-01');
const modal = parent.querySelector('.modal');
const controls = parent.querySelectorAll('.control');
etc.
I am working on a "manager" for selecting what crop shall be placed on a certain plot. Every crop has a completely different design and therefor their own class/object. However instead of writting >40 different lines that will instantiate that class I would like 1 line that simply contains the string that matches the exact name of a class and then run it. That way my code will stay clean. I have tried some stuff but never managed to get it done. Usually resulting in an the following error:
TypeError: this.crop is not a constructor
The code I am attempting to run
export default class CropManager extends Phaser.Group {
constructor (game, className, plotId) {
super(game)
this.x = 0
this.y = 0
this.plotId = plotId
this.className = className
this.cropHandler(this.className)
}
// Defines which class to call
cropHandler (className) {
const ActualClass = 'plot' + className
this.cropclasses = { ActualClass: ActualClass}
this.crop = this.cropclasses[ActualClass]
this.classRun = new this.crop(this.game, this.x, this.y, this.plotId)
this.add(this.classRun)
}
}
Note every crop their classname = crop+cropname (cropCarrots, cropCows, etc)
Rethink the way you're storing key-value pairs in this.cropclasses. The way it's done now, it's going to have 'ActualClass' as the key and 'plotNameOfTheClass' (or whatever 'plot' + className produces) as the value, thus, when accessing it later as an array, this.crop comes out undefined since there isn't a 'plotNameOfTheClass' key in the map.
I am looking for a way to retrieve the style from an element that has a style set upon it by the style tag.
<style>
#box {width: 100px;}
</style>
In the body
<div id="box"></div>
I'm looking for straight javascript without the use of libraries.
I tried the following, but keep receiving blanks:
alert (document.getElementById("box").style.width);
alert (document.getElementById("box").style.getPropertyValue("width"));
I noticed that I'm only able to use the above if I have set the style using javascript, but unable to with the style tags.
The element.style property lets you know only the CSS properties that were defined as inline in that element (programmatically, or defined in the style attribute of the element), you should get the computed style.
Is not so easy to do it in a cross-browser way, IE has its own way, through the element.currentStyle property, and the DOM Level 2 standard way, implemented by other browsers is through the document.defaultView.getComputedStyle method.
The two ways have differences, for example, the IE element.currentStyle property expect that you access the CCS property names composed of two or more words in camelCase (e.g. maxHeight, fontSize, backgroundColor, etc), the standard way expects the properties with the words separated with dashes (e.g. max-height, font-size, background-color, etc).
Also, the IE element.currentStyle will return all the sizes in the unit that they were specified, (e.g. 12pt, 50%, 5em), the standard way will compute the actual size in pixels always.
I made some time ago a cross-browser function that allows you to get the computed styles in a cross-browser way:
function getStyle(el, styleProp) {
var value, defaultView = (el.ownerDocument || document).defaultView;
// W3C standard way:
if (defaultView && defaultView.getComputedStyle) {
// sanitize property name to css notation
// (hypen separated words eg. font-Size)
styleProp = styleProp.replace(/([A-Z])/g, "-$1").toLowerCase();
return defaultView.getComputedStyle(el, null).getPropertyValue(styleProp);
} else if (el.currentStyle) { // IE
// sanitize property name to camelCase
styleProp = styleProp.replace(/\-(\w)/g, function(str, letter) {
return letter.toUpperCase();
});
value = el.currentStyle[styleProp];
// convert other units to pixels on IE
if (/^\d+(em|pt|%|ex)?$/i.test(value)) {
return (function(value) {
var oldLeft = el.style.left, oldRsLeft = el.runtimeStyle.left;
el.runtimeStyle.left = el.currentStyle.left;
el.style.left = value || 0;
value = el.style.pixelLeft + "px";
el.style.left = oldLeft;
el.runtimeStyle.left = oldRsLeft;
return value;
})(value);
}
return value;
}
}
The above function is not perfect for some cases, for example for colors, the standard method will return colors in the rgb(...) notation, on IE they will return them as they were defined.
I'm currently working on an article in the subject, you can follow the changes I make to this function here.
I believe you are now able to use Window.getComputedStyle()
Documentation MDN
var style = window.getComputedStyle(element[, pseudoElt]);
Example to get width of an element:
window.getComputedStyle(document.querySelector('#mainbar')).width
In jQuery, you can do alert($("#theid").css("width")).
-- if you haven't taken a look at jQuery, I highly recommend it; it makes many simple javascript tasks effortless.
Update
for the record, this post is 5 years old. The web has developed, moved on, etc. There are ways to do this with Plain Old Javascript, which is better.
Use getComputedStyle function, Computed style contains all the CSS properties set to an element. Even if do not set a property to an element. You will still find that property in the computed styles.
Example:
<style>
#Body_element {
color: green;
}
</style>
<body id="Body_element">
<script>
alert(getComputedStyle(Body_element).color)
</script>
</body>
This is a helper function if you want to get multiple style rules from the same element.
You pass it the element and the styles you want as arguments, and it will return their values
const convertRestArgsIntoStylesArr = ([...args]) => {
return args.slice(1);
}
const getStyles = function () {
const args = [...arguments];
const [element] = args;
let stylesProps = [...args][1] instanceof Array ? args[1] : convertRestArgsIntoStylesArr(args);
const styles = window.getComputedStyle(element);
const stylesObj = stylesProps.reduce((acc, v) => {
acc[v] = styles.getPropertyValue(v);
return acc;
}, {});
return stylesObj;
};
Now, you can use this function like this:
const styles = getStyles(document.body, "height", "width");
OR
const styles = getStyles(document.body, ["height", "width"]);