Build JavaScript Object to use with jQuery .css() (what about duplicate keys?) - javascript

I use jQuery's .css() method to apply styles to an element. I do this like so:
var cssObj = {
'background-color' : '#000',
'background-image': '-webkit-linear-gradient(top,#000,#fff)',
'background-image': 'linear-gradient(top,#000,#fff)'
};
$(".element").css(cssObj);
The problem with this is that obviously I use duplicate keys in the object, which is not cool.
How can I solve this problem? I need to pass the CSS params with duplicate names to address most browsers.

Having multiple keys with the same name is not valid, and will generate an error in strict mode.
Create a function/plugin which applies the properties of your cssObj. If a string-string pair is found, set a CSS property with the desired value.
If an array is found, loop through it, and update the property with each value. If an invalid value is found, it's ignored.
DEMO: http://jsfiddle.net/RgfQw/
// Created a plugin for project portability
(function($){
$.fn.cssMap = function(map){
var $element = this;
$.each(map, function(property, value){
if (value instanceof Array) {
for(var i=0, len=value.length; i<len; i++) {
$element.css(property, value[i]);
}
} else {
$element.css(property, value);
}
});
}
})(jQuery);
// Usage:
var cssObj = {
'background-color': '#000',
'background-image': ['-webkit-linear-gradient(top,#000,#fff)',
'linear-gradient(top,#000,#fff)']
};
$(".element").cssMap(cssObj);

My advice would be to put your CSS into your stylesheet in it's own class, and simply add that class to your element instead. The browser itself will determine which of the background-image properties it supports, and will therefore only render that one.
CSS
.gradient-bg {
background-color: #000;
background-image: -webkit-linear-gradient(top, #000, #fff);
background-image: linear-gradient(top, #000, #fff)
}
jQuery
$(".element").addClass("gradient-bg");

You need to create a custom $.cssHooks (more info) that determines which one is the correct form,
by doing various tests --- or you could just use a css class with $.fn.addClass.

There is a repository of cssHooks on github for most properties. Writing your own hook is tricky, lots of edge cases.
https://github.com/brandonaaron/jquery-cssHooks
For background image gradient, you'd need:
https://github.com/brandonaaron/jquery-cssHooks/blob/master/gradients.js

It looks like you imply that .css() method works like CSS attributes in a .css file. I don't think it works like that. But here are few alternatives:
Use browser sniffing (why not? you already doing multi-browser CSS with vendor prefixes)
Use actual stylesheet linked as <link /> tag
Create <style> tag and add rules to it, dynamically.
Use style attribute: $('#my').attr('style', 'background: ...; bakground: ...; border-radius: ...; -moz-border-radius: ...;');

Related

How to set CSS style property in JavaScript with fallback?

In CSS, I have e.g. the following:
background-image: url("IMAGE_URL"); /* fallback */
background-image: url("IMAGE_URL"), linear-gradient(#eb01a5, #d13531); /* W3C */
I want to achieve the same effect in JavaScript. I.e. I want to set the backgroundImage property, but I also want to set a fallback. But element.style.backgroundImage does not accept a string array.
element.style.backgroundImage = 'url("IMAGE_URL"), linear-gradient(#eb01a5, #d13531)'; // How to include the fallback here?
I don't want to use hacks to check which browser the user is using. If it's not possible, I would also like to know.
Just append a <style> element using JavaScript, and insert the CSS you would have used that way:
document.head.innerHTML += `
<style>
element {
background-image: url("IMAGE_URL"); /* fallback */
background-image: url("IMAGE_URL"), linear-gradient(#eb01a5, #d13531); /* W3C */
}
</style>
`
You can probably use CSS.supports and find the first item in an array of values that returns true. Then apply that to style.
Note: not all properties can be detected - notably whether flex gap is supported. Flex box gap is recent but grid gap has been there a while so testing for ‘gap’ is ambiguous.
https://developer.mozilla.org/en-US/docs/Web/API/CSS/supports
This is effectively what the browser is doing when you provide a list.

document.body.style.marginTop returning blank string in JS

It was my understanding that [some elem].style.maginTop would return a string with the element's top margin.
Instead, I'm always getting a blank string. I want to use this with the body, but I also tried on a div, and that didn't work either.
console.log(document.body.style.marginTop); // logs ""
console.log(typeof(document.body.style.marginTop)); // logs "String"
var elem = document.getElementById("testDiv");
console.log(elem.style.marginTop); // logs ""
body {
margin-top:100px;
}
#testDiv {
margin-top:50px;
}
hi!
<div id="testDiv">test</div>
I'm not sure what I'm doing wrong... Does anybody have a non-jQuery solution to this?
The HTMLElement.style only returns inline styles:
The HTMLElement.style property returns a CSSStyleDeclaration object that represents the element's style attribute.
To access the styles from stylesheets use Window.getComputedStyle(element):
The Window.getComputedStyle() method gives the values of all the CSS properties of an element after applying the active stylesheets and resolving any basic computation those values may contain.
var elem = document.getElementById("testDiv");
var style = window.getComputedStyle(elem);
//output
document.body.innerHTML = style.marginTop;
body {
margin-top:100px;
}
#testDiv {
margin-top:50px;
}
hi!
<div id="testDiv">test</div>
You can use getComputedStyle and getPropertyValue to get top margin.
console.log(document.body.style.marginTop); // logs ""
console.log(typeof(document.body.style.marginTop)); // logs "String"
var elem = document.getElementById("testDiv");
//console.log(elem.style.marginTop); // logs ""
console.log(getComputedStyle(elem).getPropertyValue("margin-top"));
alert(getComputedStyle(elem).getPropertyValue("margin-top"));
body {
margin-top:100px;
}
#testDiv {
margin-top:50px;
}
hi!
<div id="testDiv">test</div>
Tested and working.
I don't know why, but I got it working by implicitly assigning the margin-top by JS first. I know why my answer works (it's been over 2 years so I better know why). By setting the values of div#test and body to 50px and
100px like this:
document.getElementById("testDiv").style.marginTop = '50px';
document.body.style.marginTop = '100px';
I'm actually setting the CSS property/value of the elements inline:
<body style='margin-top: 100px'>
<div id='testDiv' style='margin-top: 50px'>test</div>
</body>
Whenever the .style property is used, the CSS property/value that follows it is always inline. One important thing to remember about inline CSS styles is that they have a higher priority than the other 2 means of CSS Declaration: external stylesheets (ex. <link href="file.css"...) and inline stylesheet (ex. <style>...</style>). The only way to override an inline style is to use !important (unless of course the inline style has !important as well.)
So if the.style property is used to read a property/value of an element, it'll only return the inline style value if it actually exists which in OP's case it never did and in my case it did because I used .style to assign the property/values. While my solution is correct, the answers by Nicolo and Mr. Karlsson are better since you'll get the values from all CSS stylesheets.
document.getElementById("testDiv").style.marginTop = '50px';
document.body.style.marginTop = '100px';
console.log(document.getElementById("testDiv").style.marginTop);
console.log(document.body.style.marginTop);
body {
margin-top: 100px;
}
#testDiv {
margin-top: 50px;
}
hi!
<div id="testDiv">test</div>
Hm, I tried it as well with the same result as you did. A quick google search turned up Using JavaScript to read html / body tag margin-top which uses style.getPropertyValue() to return the information you're looking for.
instead of using core javascript, let you use js library jQuery. Then use this syntaxt to get its value.:
console.log($('body').css('margin-top'));
for pure javascript use this
var element = document.body,
style = window.getComputedStyle(element),
margin_top = style.getPropertyValue('margin-top');
console.log(margin_top);
http://jsfiddle.net/hAw53/726/

How to find all css classes and its css attributes inside a certain div?

i would like to find all classes and ids inside a certain div ! and these css attributes!
Example :
<div class="demo">
<div class="new_class">
<p id="para">This is Demo Paragraph</p>
<a style="background:#ccc">HyperLink</a>
</div>
</div>
<style>
.demo{
height:100px; width:100px; background:#FF0;
}
.new_class{height:40px; width:40px; background:#999;}
#para{color:#E1E1E1;}
</style>
Now The question is that: i would like to find all classes and ids which are used inside demo class ! and Their css values too(which style applying now. ).
I would like to find result as below :
<style>
.demo{
height:100px; width:100px; background:#FF0;
}
.new_class{height:40px; width:40px; background:#999;}
#para{color:#E1E1E1;}
a{background:#ccc;}
</style>
OP, not sure what your purpose is, but in general, this can be useful. I had a project where I needed to embed a fancy template from one site onto a page on a different site with a very different, and conflicting stylesheet. I used some code similar to the following to grab every applied style from the original content, via document.styleSheets, then reapplied them all as inline styles, so I could put it onto the "parent" site without the stylesheets conflicting.
Fiddle
JS
var selector,rule;
var result=[];
var sheets = document.styleSheets;
for (var i in sheets) {
//rules or cssRules, depending on the browser
var rules = sheets[i].rules || sheets[i].cssRules;
//iterate over every css rule in the document
for (var r in rules)
{
selector=rules[r].selectorText;
rule=rules[r].cssText;
//select demo itself, as well as all of its children
$('.demo, .demo *').each(function () {
//console.log($(this),selector);
//for each element, see if it matches the current rule. add if it does
if ($(this).is(selector))
{
result.push(rule);
}
});
}
}
console.log(result);
//result[0] .demo { height: 100px; width: 100px; background: none repeat scroll 0% 0% rgb(255, 255, 0); }
//result[1] .new_class { height: 40px; width: 40px; background: none repeat scroll 0% 0% rgb(153, 153, 153); }
//result[2] #para { color: rgb(225, 225, 225); }
Granted, you will have to tweak this on your own to do things like, removing duplicate styles that would occur if you were to apply this to a larger block of HTML, and for dealing with inline styles (which this does not attempt to do, but you can get them from the style attribute and work from there...), and possibly the computed style, which you can get with getComputedStyle, as indicated by the #Derek's answer. but this should get you started.
To find all existing id, try:
var ids = [];
$(".demo *").each(function(){ this.id && ids.push(this.id); });
console.log(ids);
Do the same thing for class or anything else.
However, to get your expected output, you must first acquire the defined CSS style for each element. Which one should be included? p by default gets margins and paddings. Do you include those too? You will also need to dig into all the CSS declarations just to find the style that are applied, which is almost impossible to do.
For example,
<div class="yellow"></div>
<style>
div.yellow:not(.blue){
background: yellow;
}
</style>
How do you get the background of the <div> tag? .style.background? Nah, it returns "". Well now you will have to reach into the CSS declaration with document.styleSheets to see which one applied. How do you even check if the rule div.yellow:not(.blue) matches your element? Good luck doing that. (There might be libraries that does this kind of thing, or maybe you can even utilize jQuery's internal selector engine with .is, though it will not be the same as in CSS) Another thing you can do is try getComputedStyle. It gives you every single computed styles that aren't even in your declaration. So what you are trying to do is not possible to do. (I don't even know what you are doing something like this.)

change background using javascript

I have to change the background of a div using JavaScript. I have managed to do that,
using document.getElementById('test').style.background = "url('img/test.jpg')";
Now, how do i change other properties like repeat, scroll,etc?
The css i want for the test is like
background: #f00 url('img/test.jpg') no-repeat fixed 10px 10px;
I cannot use jQuery, since I do not want to include the library for only a small thing.
Instead of setting all the css properties with javascript. I would suggest to create an additional css rule for this element with certain class. And then use javascript to add or remove this class from this element when you need it.
Eg.
function changeBG(){
var element = document.getElementById('test');
element.setAttribute("class", 'newBG'); //For Most Browsers
element.setAttribute("className", 'newBG'); //For IE; harmless to other browsers.
}
Below should work:
document.getElementById('test').style.background = "#f00 url('img/test.jpg') no-repeat fixed 10px 10px"
Or you can use individual properties such as backgroundColor of style object. See here for various properties of style object.
Make a class with those properties, and then just assign/remove that class through javascript.
function displayResult()
{
document.body.style.background="#f3f3f3 url('img_tree.png') no-repeat right top";
}
See following:
http://www.w3schools.com/jsref/prop_style_background.asp
As everyone suggests I also prefer using a class, but if you insist you can use JS for this as you use CSS
document.getElementById('test').style.background = "url('img/test.jpg') no-repeat fixed";
Use next style properties for changing background:
document.getElementById('test').style.background
document.getElementById('test').style.backgroundAttachment
document.getElementById('test').style.backgroundClip
document.getElementById('test').style.backgroundColor
document.getElementById('test').style.backgroundImage
document.getElementById('test').style.backgroundOrigin
document.getElementById('test').style.backgroundPosition
document.getElementById('test').style.backgroundPositionX
document.getElementById('test').style.backgroundPositionY
document.getElementById('test').style.backgroundRepeat
document.getElementById('test').style.backgroundSize
http://msdn.microsoft.com/en-us/library/ms535240%28v=vs.85%29.aspx
This will give the class to the dom element
document.getElementById('test').className = 'cssName'

Changing CSS pseudo-element styles via JavaScript [duplicate]

This question already has answers here:
Selecting and manipulating CSS pseudo-elements such as ::before and ::after using javascript (or jQuery)
(26 answers)
How to update placeholder color using Javascript?
(5 answers)
Closed 2 years ago.
Is it possible to change a CSS pseudo-element style via JavaScript?
For example, I want to dynamically set the color of the scrollbar like so:
document.querySelector("#editor::-webkit-scrollbar-thumb:vertical").style.background = localStorage.getItem("Color");
and I also want to be able to tell the scrollbar to hide like so:
document.querySelector("#editor::-webkit-scrollbar").style.visibility = "hidden";
Both of these scripts, however, return:
Uncaught TypeError: Cannot read property 'style' of null
Is there some other way of going about this?
Cross-browser interoperability is not important, I just need it to work in webkit browsers.
If you're comfortable with some graceful degradation in older browsers you can use CSS Vars. Definitely the easiest of the methods I've seen here and elsewhere.
So in your CSS you can write:
#editor {
--scrollbar-background: #ccc;
}
#editor::-webkit-scrollbar-thumb:vertical {
/* Fallback */
background-color: #ccc;
/* Dynamic value */
background-color: var(--scrollbar-background);
}
Then in your JS you can manipulate that value on the #editor element:
document.getElementById("#editor").style.setProperty('--scrollbar-background', localStorage.getItem("Color"));
Lots of other examples of manipulating CSS vars with JS here: https://eager.io/blog/communicating-between-javascript-and-css-with-css-variables/
To edit an existing one which you don't have a direct reference to requires iterating all style sheets on the page and then iterating all rules in each and then string matching the selector.
Here's a reference to a method I posted for adding new CSS for pseudo-elements, the easy version where you're setting from js
Javascript set CSS :after styles
var addRule = (function (style) {
var sheet = document.head.appendChild(style).sheet;
return function (selector, css) {
var propText = typeof css === "string" ? css : Object.keys(css).map(function (p) {
return p + ":" + (p === "content" ? "'" + css[p] + "'" : css[p]);
}).join(";");
sheet.insertRule(selector + "{" + propText + "}", sheet.cssRules.length);
};
})(document.createElement("style"));
addRule("p:before", {
display: "block",
width: "100px",
height: "100px",
background: "red",
"border-radius": "50%",
content: "''"
});
sheet.insertRule returns the index of the new rule which you can use to get a reference to it for it which can be used later to edit it.
EDIT: There is technically a way of directly changing CSS pseudo-element styles via JavaScript, as this answer describes, but the method provided here is preferable.
The closest to changing the style of a pseudo-element in JavaScript is adding and removing classes, then using the pseudo-element with those classes. An example to hide the scrollbar:
CSS
.hidden-scrollbar::-webkit-scrollbar {
visibility: hidden;
}
JavaScript
document.getElementById("editor").classList.add('hidden-scrollbar');
To later remove the same class, you could use:
document.getElementById("editor").classList.remove('hidden-scrollbar');
I changed the background of the ::selection pseudo-element by using CSS custom properties doing the following:
/*CSS Part*/
:root {
--selection-background: #000000;
}
#editor::selection {
background: var(--selection-background);
}
//JavaScript Part
document.documentElement.style.setProperty("--selection-background", "#A4CDFF");
You can't apply styles to psuedo-elements in JavaScript.
You can, however, append a <style> tag to the head of your document (or have a placeholding <style id='mystyles'> and change its content), which adjusts the styles. (This would work better than loading in another stylesheet, because embedded <style> tags have higher precedence than <link>'d ones, making sure you don't get cascading problems.
Alternatively, you could use different class names and have them defined with different psuedo-element styles in the original stylesheet.
I posted a question similar to, but not completely like, this question.
I found a way to retrieve and change styles for pseudo elements and asked what people thought of the method.
My question is at Retrieving or changing css rules for pseudo elements
Basically, you can get a style via a statement such as:
document.styleSheets[0].cssRules[0].style.backgroundColor
And change one with:
document.styleSheets[0].cssRules[0].style.backgroundColor = newColor;
You, of course, have to change the stylesheet and cssRules index. Read my question and the comments it drew.
I've found this works for pseudo elements as well as "regular" element/styles.
An old question, but one I came across when try to dynamically change the colour of the content of an element's :before selector.
The simplest solution I can think of is to use CSS variables, a solution not applicable when the question was asked:
"#editor::-webkit-scrollbar-thumb:vertical {
background: --editorScrollbarClr
}
Change the value in JavaScript:
document.body.style.setProperty(
'--editorScrollbarClr',
localStorage.getItem("Color")
);
The same can be done for other properties.
Looks like querySelector won't work with pseudo-classes/pseudo-elements, at least not those. The only thing I can think of is to dynamically add a stylesheet (or change an existing one) to do what you need.
Lots of good examples here:
How do I load css rules dynamically in Webkit (Safari/Chrome)?

Categories

Resources