I have some code:
$("#" + this.id).css("border-radius",this.radius + "px");
$("#" + this.id).css("-moz-border-radius",this.radius + "px");
$("#" + this.id).css("-webkit-border-radius",this.radius + "px");
I am trying to improve lines like this by using JSON to apply them (as shown in jQuery's docs) or by removing the vendor prefix versions completely.
Does jQuery's .css() method automatically apply any required vendor prefixes when changing CSS properties on an element?
As #zeroflagL wrote it appears that since jQuery 1.8.0 .css() does add browser specific prefixes (see this).
In earlier versions this is not done automatically by jQuery's .css(). You will have to do it by yourself or you can use jQuery's .cssHooks() to add vendor prefixes.
Code example from here:
(function($) {
if ( !$.cssHooks ) {
throw("jQuery 1.4.3+ is needed for this plugin to work");
return;
}
function styleSupport( prop ) {
var vendorProp, supportedProp,
capProp = prop.charAt(0).toUpperCase() + prop.slice(1),
prefixes = [ "Moz", "Webkit", "O", "ms" ],
div = document.createElement( "div" );
if ( prop in div.style ) {
supportedProp = prop;
} else {
for ( var i = 0; i < prefixes.length; i++ ) {
vendorProp = prefixes[i] + capProp;
if ( vendorProp in div.style ) {
supportedProp = vendorProp;
break;
}
}
}
div = null;
$.support[ prop ] = supportedProp
return supportedProp;
}
// check for style support of your property
// TODO by user: swap out myCssPropName for css property
var myCssPropName = styleSupport("myCssPropName");
// set cssHooks only for browsers that
// support a vendor-prefixed border radius
if (myCssPropName && myCssPropName !== 'myCssPropName') {
$.cssHooks["myCssPropName"] = {
get: function(elem, computed, extra) {
// handle getting the CSS property
return $.css(elem, myCssPropName);
},
set: function(elem, value) {
// handle setting the CSS value
elem.style[myCssPropName] = value;
}
};
}
})(jQuery);
jQuery DOES add vendor prefixes. It first checks for the presence of the standard property and if it's not found for a vendor prefixed version. From the source:
// return a css property mapped to a potentially vendor prefixed property
function vendorPropName( style, name ) {
// shortcut for names that are not vendor prefixed
if ( name in style ) {
return name;
}
// check for vendor prefixed names
...
I don't know since which version, but I think 1.8.
This is now confirmed in the official docs: http://api.jquery.com/css/
Setter ( .css( propertyName, value ) )
As of jQuery 1.8, the .css() setter will automatically take care of prefixing the property name. For example, take .css( "user-select", "none" ) in Chrome/Safari will set it as -webkit-user-select, Firefox will use -moz-user-select, and IE10 will use -ms-user-select.
Getter ( .css( propertyName ) )
The .css() method is a convenient way to get a computed style property from the first matched element, especially in light of the different ways browsers access most of those properties (the getComputedStyle() method in standards-based browsers versus the currentStyle and runtimeStyle properties in Internet Explorer prior to version 9) and the different terms browsers use for certain properties. For example, Internet Explorer's DOM implementation refers to the float property as styleFloat, while W3C standards-compliant browsers refer to it as cssFloat. For consistency, you can simply use "float", and jQuery will translate it to the correct value for each browser.
It doesn't explicitly mention vendor prefixes in the getter context but it's easy to test. For example, $element.css('border-radius') on Chrome returns values set as border-radius or -webkit-border-radius and ignores values set as -moz-border-radius.
Just keep in mind that it's inconsistent across browsers for shorthand properties:
Retrieval of shorthand CSS properties (e.g., margin, background, border), although functional with some browsers, is not guaranteed. For example, if you want to retrieve the rendered border-width, use: $( elem ).css( "borderTopWidth" ), $( elem ).css( "borderBottomWidth" ), and so on.
Related
I'd like to keep track of changes that I make in chrome's dev tools on a website. I was wondering if chrome or javascript has any way of keeping track of this via a chrome plugin or something.
I was thinking one thing you might be able to do is store a page's HTML, and then when you're doing making changes, diff the current HTML with what was stored. This would allow you to keep track of the styling changes that were made on element.style because they get applied as inline styles right on the element like shown below where I add color:red;:
But what about keeping track of style changes that are made to elements who's styles are being manipulated through a stylesheet like this? (where I added color:white;background:blue;
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.
The demonstration below omits keeping track of pseudo-elements, but that functionality could be shoehorned in using window.getComputedStyle(element[, pseudoElt]); where:
element
The Element for which to get the computed style.
pseudoElt Optional
A string specifying the pseudo-element to match. Must be omitted (or null) for regular elements.
Usage
Add the code to the HTML document you're working on, via <script> or the developer console.
Use setInitStyles() to set or reset the state of the styles of each of the elements, which is compared against when calling differenceEngine().
Use differenceEngine() to get the difference between the styles set when last calling setInitStyles() and now.
Example
This code would benefit from some optimization, and alteration to taste i.e. the output format may be undesirable.
The snippet will run as expected (tested in Chrome), but since large amounts of data are logged to the console, I disabled the snippet's console (for efficiency) so you'll need to see the output in your browser's.
const compressedStyles = ( e ) => {
const ucs = window.getComputedStyle( e );
var s, cs = {};
for ( s in ucs ) {
if ( ucs.hasOwnProperty( s ) && /[^0-9]+/.test( s ) ) {
cs[ s.replace( /([A-Z])/g, "-$1" ).toLowerCase() ] = ucs[ s ];
}
}
return cs;
},
setElementStyles = ( e ) => {
e.stylesInit = compressedStyles( e );
e.stylesDiff = {}; // while we're here
},
allTheThings = () => {
var att = Array.from( document.body.querySelectorAll( "*:not( script )" ) );
att.unshift( document.body );
return att;
},
setInitStyles = () => {
allTheThings().forEach( setElementStyles );
},
differenceEngine = () => {
allTheThings().forEach( ( e ) => {
if ( e.stylesInit ) {
const cs = compressedStyles( e );
var s, css, ess;
for ( s in e.stylesInit ) {
ess = e.stylesInit[ s ].toString();
css = cs[ s ].toString();
if ( ess != css ) {
e.stylesDiff[ s ] = { "curr": css, "init": ess };
}
}
console.log( e, e.stylesDiff );
} else {
setElementStyles( e ); // set current styles on new elements
}
} );
};
console.info( "Setting the initial styles" );
setInitStyles();
console.info( "Changing the style of one of the elements" );
document.querySelector( "div" ).style.border = "2px solid red";
console.info( "What's the difference?" );
differenceEngine();
console.info( "Resetting the style of the element" );
document.querySelector( "div" ).removeAttribute( "style" );
console.info( "What's the difference?" );
differenceEngine();
console.info( "Changing the class of one of the elements" );
document.querySelector( "p" ).classList.add( "foo" );
console.info( "What's the difference?" );
console.warn( "Properties that inherit from color have also changed" );
differenceEngine();
console.info( "Resetting the class of the element" );
document.querySelector( "p" ).classList.remove( "foo" );
console.info( "What's the difference?" );
differenceEngine();
p.foo {
color: red;
}
<div>
<p>Foo</p>
<p>Bar</p>
<p>Baz</p>
</div>
Made with love of the chase, as a suggestion, not a final solution.
Possible Chrome DevTools Extension
Since getComputedStyle() does exactly that, the results of the above can be a little less than helpful.
Turning our attention to the actual DevTools, we can inspect them (when popped out and focussed) and run scripts on the inspector.
This is a step toward extending the DevTools with a Chrome Extension.
Whilst pondering the build process, I stumbled upon SnappySnippet; a Chrome Extension inspired by a Stack Overflow question that supposedly (I have not used it) makes the creation of Snippets from web pages easy.
That extension may provide functionality that would effectively answer your question, but in case it doesn't (and for fun), I have started to work on what may become another Chrome Extension.
Be aware, this process may be long, slow and fruitless; if at any point I create anything more useful than the code below, I will return to update this answer.
I hereby present my latest kludge! \o/
document.querySelectorAll( ".styles-section" ).forEach( ( e ) => {
var output;
const selector = e.querySelector( ".selector" ).textContent,
properties = e.querySelector( ".style-properties" )
.shadowRoot.querySelectorAll( ".tree-outline > li" );
if ( properties ) {
output = selector + " {\n";
properties.forEach( ( p ) => {
const property = p.querySelector( ".webkit-css-property" ).textContent,
value = p.querySelector( ".value" ).textContent;
if ( p.classList.contains( "inactive" ) ) {
output += "\t/* " + property + ": " + value + "; */\n";
} else {
output += "\t" + property + ": " + value + ";\n";
}
} );
}
console.log( output + "}" );
} );
This code, when run on an inspector on an inspector (not a typo), will spit out a pretty copy of the content of the styles pane for the currently selected element in the inspector of the original HTML.
Mhmm - kludge.
Instead of spitting out a bunch of text, it could be relatively simply tweaked to spit out an object of data, which like in the code of my first response, could be compared with other snapshots to get the difference.
And, since this code is being run on the inspector, and not on the document being manipulated, it is a solid step toward exactly what we could achieve with a DevTools Extension.
I will continue fiddling with this, and update this answer as I go, but don't hold your breath.
Manual Solution (in lieu of wizardry)
Although not even remotely high-tech, there is a very simple and reliable method to keep track of changes one makes to element.style and CSS from resources or <style>sheets:
As you can see in the screenshot above, we can add comments to any value of any property we change or add, showing what the value used to be, or that the value is entirely new.
And of course, if we want to remove anything, we can just uncheck the checkbox to the left of the property, so the value is maintained.
Consider that I create some custom elements with HTML5
<orange-juice>...</orange-juice>
<apple-juice>...</apple-juice>
<banana-juice>...</banana-juice>
There are many type of juice elements. And I want to select them with a single instruction with jQuery using their suffix.
I try that but it does not work :
$('$=juice').html('juice'); //the .html instruction is not important
If i take them one by one this work.
$('orange-juice').html('juice'); //this work
$('apple-juice').html('juice'); //this work
$('banana-juice').html('juice'); //this work
But there are many of these custom element suffixed by juice. How can I select them in one instruction.
EDIT 1
It's sure that a common class will work but, it's not my code and there are too many of these elements to take theme one by one.
But if no solution then, I will make this (during a month).
You can try .filter(fn) function, Here is an example of prefix
$('body *').filter(function() {
return this.tagName.toLowerCase().indexOf('juice') == 0;
}).html('juice');
However I would recommend, you to assign a common class then Class Selector (“.class”) can be easily used.
Example of Suffix, Here I have used endsWith() method
jQuery(function($) {
$('body *').filter(function() {
return this.tagName.toLowerCase().endsWith('juice');
}).html('juice');
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<orange-juice>...</orange-juice>
<apple-juice>...</apple-juice>
<banana-juice>...</banana-juice>
While you've already accepted a jQuery solution to the problem, which is what you asked for, it's worth also adding – if only for the sake of completion – a plain JavaScript approach for the same.
// declaring an object to contain the two functions:
let findElementsBy = {
// retrieving all elements held within the <body> element,
// we could instead use:
// document.getElementsByTagName('*')
// but this is just personal preference:
'allElems': document.querySelectorAll('body *'),
// declaring the 'suffix' function:
// ending: String, a required argument which is 'ending'
// by which we're filtering the retrieved elements:
'suffix': function(ending) {
// here we use Array.from() to convert the Array-like
// NodeList into an Array:
return Array.from(this.allElems)
// we filter that Array using Array.prototype.filter():
.filter(
// here we use an Arrow function to keep only those
// elements ('el', the current Array-element of the
// Array over which we're iterating) whose lower-case
// tagName ends with the supplied 'ending' String,
// determined using String.prototype.endsWith(),
// which returns a Boolean:
el => el.tagName.toLowerCase().endsWith(ending)
// this filtered Array is then passed back to the
// calling context as an Array, which allows that
// context to iterate through the returned elements
// using Array methods.
);
},
'prefix': function(beginning) {
return Array.from(this.allElems)
.filter(
// this function is exactly the same as the above,
// but here we use String.prototype.startsWith()
// to find those elements whose lower-cased tagName
// begins with the supplied String:
el => el.tagName.toLowerCase().startsWith(beginning)
);
}
}
findElementsBy.suffix('juice').forEach(e => e.style.borderColor = 'limegreen');
findElementsBy.prefix('banana').forEach(e => e.style.backgroundColor = '#ffa');
let findElementsBy = {
'allElems': document.querySelectorAll('body *'),
'suffix': function(ending) {
return Array.from(this.allElems)
.filter(
el => el.tagName.toLowerCase().endsWith(ending)
);
},
'prefix': function(beginning) {
return Array.from(this.allElems)
.filter(
el => el.tagName.toLowerCase().startsWith(beginning)
);
}
}
findElementsBy.suffix('juice').forEach(e => e.style.borderColor = 'limegreen');
findElementsBy.prefix('banana').forEach(e => e.style.backgroundColor = '#ffa');
orange-juice,
apple-juice,
banana-juice {
display: block;
border: 1px solid transparent;
margin: 1em auto 0 auto;
width: 80%;
}
<orange-juice>...</orange-juice>
<apple-juice>...</apple-juice>
<banana-juice>...</banana-juice>
JS Fiddle demo.
References:
Array.from().
Array.prototype.filter().
Array.prototype.forEach().
Arrow functions.
document.getElementsByTagName()
document.querySelectorAll().
String.prototype.endsWith().
String.prototype.toLowerCase().
String.prototype.startsWith().
I saw the below code in transition.js, which is part of bootstrap:
function transitionEnd() {
var el = document.createElement('bootstrap')
var transEndEventNames = {
WebkitTransition : 'webkitTransitionEnd',
MozTransition : 'transitionend',
OTransition : 'oTransitionEnd otransitionend',
transition : 'transitionend'
}
for (var name in transEndEventNames) {
if (el.style[name] !== undefined) {
return { end: transEndEventNames[name] }
}
}
return false;
}
I understand how this works and I even tested this in Mozilla, the result I get in the console is as follows :
Object { end: "transitionend" }
I believe in different browsers the result will be different. Can somebody tell me when the condition if (el.style[name] !== undefined) passes or fails?
Also why the return false in the end, it says only for I.E 8, but can somebody tell me why?
The function is checking whether each of the style properties listed in transEndEventNames exists in the current browser and returns when it finds the first match.
WebKit based browsers (Chrome, Safari, etc) will return for the first item in the collection: webkitTransitionEnd.
The first 3 items in the list are vendor specific CSS properties. Those being Webkit, Mozilla and Opera. The last item in the list is the CSS standard property name as defined by W3C.
IE8 will return false because it doesn't support any of the CSS properties listed in the test object.
I believe they mention IE8 specifically because other browsers would treat a non return from a function as undefined which is a falsey value (If you do a boolean check against undefined it will be treated similarly to the value false). The explicit return false would therefore be for browsers that don't return a falsey value when no return value is specified.
In an application where certain elements have custom CSS properties, is there any way to retrieve such a value via JavaScript?
e.g.
<div id="myDiv" style="color:#f00;-my-custom-property:upsidedown;" />
I can access the color attribute via these two methods:
document.getElementById('myDiv').style.getPropertyValue("color")
document.getElementById('myDiv').style.color
But these do not work for custom properties. Is this supported at all?
CSS values not understood by the browser are discarded, which explains why -my-custom-property was unavailable via .style.
In the past, you would have had to rely on storing the data with data attributes and dealing with inheritance yourself via JavaScript.
However, "custom properties", aka "CSS variables", have since been introduced into the standard and implemented by browsers, with ~92% support globally as of 2019-05-09. At a quick glance, Edge seems to have been the last major browser to implement, with version 16 on October 16, 2017.
Essentially, you need to set a custom property (eg, --my-custom-property: 'foobar';) on an element, and it can be accessed with something like getComputedStyle(your_el).getPropertyValue("--my-custom-property") which would return 'foobar' (with a leading space). Note the leading space and quotation marks. It will return the value exactly as it was provided.
Example:
console.log(getComputedStyle(document.getElementById("a")).getPropertyValue("--my-custom-property-1"))
console.log(getComputedStyle(document.getElementById("b")).getPropertyValue("--my-custom-property-2"))
#b-div { --my-custom-property-2: 'world' }
<div style="--my-custom-property-1: 'hello'"><h1 id="a">#a 'hello'</h1></div>
<div id="b-div"><h1 id="b">#b 'world'</h1></div>
Here's some testing using one and two leading hyphens, inheritance, and different methods of retrieving the value:
function log(computed, selector, prop, value) {
let method = computed ? "getComputedStyle(el)" : "el.style"
let method_id = computed ? "computed" : "raw"
// Build first level of list (tag name)
let first = document.querySelector("#" + selector)
if (!first) {
first = document.createElement("li")
first.appendChild(document.createTextNode(selector))
first.setAttribute("id", selector)
first.appendChild(document.createElement("ul"))
document.querySelector("ul").appendChild(first)
}
// Build second level of list (method of style retrieval)
let second = document.querySelector("#" + selector + "-" + method_id)
if (!second) {
second = document.createElement("li")
second.appendChild(document.createTextNode(method))
second.setAttribute("id", selector + "-" + method_id)
second.appendChild(document.createElement("ul"))
first.querySelector("ul").appendChild(second)
}
// Build third level of list (property accessed)
let third = document.querySelector("#" + selector + "-prop" + prop)
if (!third) {
third = document.createElement("li")
third.appendChild(document.createTextNode(prop + ": `" + value + "`"))
third.setAttribute("id", "prop" + prop)
second.querySelector("ul").appendChild(third)
if (value === "") {
third.classList.add("bad")
} else {
third.classList.add("good")
}
}
}
// Uses .style
function getStyleAttr(selector, prop) {
let value = document.querySelector(selector).style.getPropertyValue(prop)
log(false, selector, prop, value)
}
// Uses getComputedStyle()
function getStyleComputed(selector, prop) {
let value = getComputedStyle(document.querySelector(selector)).getPropertyValue(prop)
log(true, selector, prop, value)
}
// Loop through each property for each element and output the value
let selectors = ["article", "h1", "p"]
let props = ["--my-custom-property", "-my-custom-property"]
selectors.forEach(function(selector) {
props.forEach(function(prop) {
getStyleAttr(selector, prop)
getStyleComputed(selector, prop)
})
})
code {
background: #eee;
padding: .2em;
}
.bad {
color: #800;
}
.good {
color: #080;
}
<article class="custom-prop-inheritance" style="--my-custom-property: 'foobar'; -my-custom-property: 'foobar'">
<h1>Title</h1>
<p>Custom properties require two leading hyphens (<code>-my-custom-property</code> <em>never</em> works). Using <code>el.style</code> does not support inheritance. To support both inheritance and custom properties, you must use <code>getComputedStyle(<b>el</b>)</code> along with two leading hyphens on the custom property (eg, <code>--my-custom-property</code>).</p>
</article>
<ul></ul>
CSS:
:root {
--custom-property: #000000;
}
Javascript:
var custom_property = window.getComputedStyle(document.body).getPropertyValue('--custom-property').trim()
Non-recognised CSS properties will be ignored when put within the style attribute, or in the style.cssText property.
If you want to define a property at a specific element, I recommend data-attributes:
HTML:
<div id="myDiv" style="color:#f00;" data-custom-property="upsidedown" />
JavaScript:
//jQuery's method to retrieve value:
$("#myDiv").data("custom-property");
//jQuery, without parsing:
$("#myDiv").attr("data-custom-property");
// Modern browsers, native JS:
document.getElementById("myDiv").dataset["custom-property"];
// Older browsers, native JS:
document.getElementById("myDiv").getAttribute("data-custom-property");
This is actually now possible for all browsers using a specialized CSS hack via the CSS content tag. This article explains how to do it:
http://www.yearofmoo.com/2015/04/cross-browser-custom-css-properties.html
function getCustomCssProperty(elementID, propertyName){
var style = document.getElementById(elementID).getAttribute("style");
var entries = style.split(";");
for (var i=0; i<entries.length; i++){
var entry = entries[i].split(":");
if(entry[0] == propertyName){
return entry[1];
}
}
return null;
}
You can't use data-* attributes (html5)?
That would at least be valid and not a strange hack.
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"]);