Detect if element has a background specified? - javascript

I'm trying to determine if an element has a background explicitly set. I figured I could just check to see if .css('background')* was set, however, it's inconsistent between browsers. For example, chrome shows an element without a background set as
background: rgba(0, 0, 0, 0) none repeat scroll 0% 0% / auto padding-box border-box
background-color: rgba(0, 0, 0, 0)
background-image: none
whereas IE8 shows
background: undefined
background-color: transparent
background-image: none
(test case here)
*(shorthand properties of CSS aren't supported for getting rendered styles in jQuery)
Short of handling each separate case is there a better way to detect this?

temporary element approach
It's not ideal, but you could create a temporary element when your js initiates, insert it somewhere hidden in the document (because if you don't you get empty styles for webkit browsers) and then read the default background style set for that element. This would give you your baseline values. Then when you compare against your real element, if they differ you know that the background has been set. Obviously the downside to this method is it can not detect if you specifically set the background to the baseline state.
var baseline = $('<div />').hide().appendTo('body').css('background');
var isBackgroundSet = ( element.css('background') != baseline );
If you wanted to avoid possible global styles on elements, that would break the system i.e:
div { background: red; }
... you could use the following instead, but I doubt if it would work so well with older browsers:
var baseline = $('<fake />').hide().appendTo('body').css('background');
background
I spent some time with a similar issue - attempting to get the original width value from an element when set to a percentage. Which was much trickier than I had assumed, in the end I used a similar temporary element solution. I also expected, as Rene Koch does above, that the getComputedStyle method would work... really annoyingly it doesn't. Trying to detect the difference between the source CSS world and the runtime CSS world is a difficult thing.

This should work:
function isBGDefined(ele){
var img = $(ele).css('backgroundImage'),
col = $(ele).css('backgroundColor');
return img != 'none' || (col != 'rgba(0, 0, 0, 0)' && col != 'transparent');
};
DEMO
I didn't bother to test against the background property because in the end, it will change the computed styles of either backgroundImage and/or backgroundColor.
Here's the code run against your test case (with another added): http://jsfiddle.net/WG9MC/4/

this article explains how:
http://robertnyman.com/2006/04/24/get-the-rendered-style-of-an-element/
function getStyle(oElm, strCssRule){
var strValue = "";
if(document.defaultView && document.defaultView.getComputedStyle){
strValue = document.defaultView.getComputedStyle(oElm, "").getPropertyValue(strCssRule);
}
else if(oElm.currentStyle){
strCssRule = strCssRule.replace(/\-(\w)/g, function (strMatch, p1){
return p1.toUpperCase();
});
strValue = oElm.currentStyle[strCssRule];
}
return strValue;
}

Using the approach suggested by #pebbl I wrote a small jQuery function, hasBack(), to determine if an element has its background set.
$.fn.hasBack = function()
{
var me = $.fn.hasBack;
if(!me.cache)
{
// get the background color and image transparent/none values
// create a temporary element
var $tmpElem = $('<div />').hide().appendTo('body');
$.fn.hasBack.cache = {
color: $tmpElem.css('background-color'),
image: $tmpElem.css('background-image')
};
$tmpElem.remove();
}
var elem = this.eq(0);
return !(elem.css('background-color') === me.cache.color && elem.css('background-image') === me.cache.image);
}
This was tested in Chrome v22, Firefox v15, Opera 12.1, IE9, IE9 set to browser modes 9 compat, 9, 8, 7 and quirks mode.
Test case here.

Related

How to get complete css of element (classes, inline, #import,#media)

I want to work with CSS styles, so I get the needed info about this HTML
<img src="imagelink" id="IMG" style="margin: 5px 4em 3% 2%;">
by this code
if(window.getComputedStyle){
style = window.getComputedStyle(dom, null);
for(var i = 0, l = style.length; i < l; i++){
var prop = style[i];
var val = style.getPropertyValue(prop);
returns[prop] = val;
}
return returns;
}
Until now, I only work with "px", so all works fine.
Now I try to work with the other units and just I get trouble.
Instead of 5px, 4em, 3%, 2%, I receive from the computedStyle recalculate absolute values for "px".
"margin-inline-start": "21.3667px"
"margin-left": "21.3667px"
"margin-right": "48px"​
"margin-top": "5px"
Why is it so and how I can prevent that? I want to work with the kind of unit which is defined in the inline CSS.
Recalculate this won't work, because I cannot see any info in the computedStyle that the original kind was not "px".
Thanks a lot for helping to understand and solving that.
UPDATE
I look around to find a way to get all matching rules for a element (css class, inline-style, #import and #media). But all what i find & read are half-workes or to old and wont work now)...i'm frustrated at this point

SVG element broke when updating to SVG 2, why?

I had an SVG element that was used to render lines connecting nodes in a flowchart. At the beginning of October 2018, it suddenly stopped working in Chrome - the SVG element has 0 width and height in the DOM even though it has width and height attributes defined.
After doing some searching I found that Chrome recently updated its standards to SVG 2, however this SVG is fairly simple and I can't figure out exactly what changes caused this to happen.
Details:
The SVG is inside a regular DIV with position:relative. The DIV appears properly and has height and width set.
The SVG has a class and used to have position:absolute. It no longer seems to have any style and I can't edit its style through DevTools. I'm not certain if it needed to have a style to begin with.
The SVG has a bunch of line elements in it, and nothing else. The lines have classes and their styles don't work either.
The parent DIV does have other DIV elements in it (the nodes in the flowchart). These elements all have position:absolute.
Neither the parent DIV nor the SVG exist when the page is opened. They are created using Javascript.
There are no other SVG elements on the page and no use of the "use" keyword anywhere.
What part of this breaks with SVG 2 compliance?
Here is the code:
function appendElement(type,className,to,inner){
if (type === 'svg' || type === 'line'){
var el = document.createElementNS("https://www.w3.org/2000/svg", type);
if (className !== undefined) el.setAttribute("class",className);
} else {
var el = document.createElement(type);
if (className !== undefined) el.className = className;
}
to.appendChild(el);
if (inner !== undefined) el.innerHTML = inner;
return el;
}
The function in the flowchart class
setInner(){
this.flowchart.innerHTML = '';
this.svg = appendElement('svg','bw-flowchart-svg',this.flowchart);
this.svg.setAttribute("width", 800);
this.svg.setAttribute("height", 500);
this.currentSize = [800,500];
this.listitems = [];
this.links = [];
for (var i in this.obj.nodes){
this.listitems.push(new BWBFlowchartNode(this,this.obj.nodes[i]));
}
for (var i in this.listitems){
this.listitems[i].createLinks();
}
this.checkSize();
}
The createLinks function adds all of the lines and sets their X and Y values. The lines are being added to the DOM properly.
And the style that should be applied (but neither the svg nor the lines have any styling at all)
.bw-flowchart-svg{
position:absolute;
}
.bw-flowchart-line{
stroke: rgb(0,0,0);
stroke-width: 1;
}
The following line is incorrect
document.createElementNS("https://www.w3.org/2000/svg", type);
The SVG namespace is actually
document.createElementNS("http://www.w3.org/2000/svg", type);
This is true in SVG 1.1 and is unchanged in SVG 2. A namespace is not actually a URL despite looking like one.

Detect support for the #-ms-viewport rule in Edge

Now that Microsoft has decided to move the #-ms-viewport rule in Edge behind an about:config flag, using javascript (not jquery) how do you determine if the user has that flag enabled (or is using an older version of Edge where it was still supported)?
Thread about the withdrawal of the rule here https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/7970618/
Maybe something like this? Please see comments in code.
function testSupportMsViewport() {
let result = false;
let s = document.createElement('style');
let test = document.createElement('div');
// create style rule to make viewport 200px in width
// if #-ms-viewport is interpreted
s.innerHTML = '#-ms-viewport { width: 200px; }';
// hide any overflow hence we do not need to take scrollbars into account
s.innerHTML += '* { overflow-y: hidden; }';
// use fixed position, block box sizing and no paddings/margins/borders
// together with a width of 1% (which is 2px when the viewport is 200px)
s.innerHTML += '.foo { position: fixed; display: block; padding: 0; border: 0; margin: 0; width: 1%; }';
test.classList += 'foo';
// append the style element and the test div to the html element
// applying the test styles to the div
// note that we're not appending it to body since there may be
// styles applied which could disturb calculations
document.documentElement.appendChild(s);
document.documentElement.appendChild(test);
// check width of test div. If the browser interprets the rule,
// it should be 2 (2px)
// obviously, this yields a false-positive when the viewport really is just 200px in width. Think about using some other value or add additional checks
if (parseFloat(window.getComputedStyle(test).width) === 2) {
result = true;
}
// Remove elements since they're not needed anymore
document.documentElement.removeChild(s);
document.documentElement.removeChild(test);
return result;
}
I was not able to test that snippet in Edge since I'm not using Windows. However, the function returns true if the viewport is 200px in width and false otherwise.
(If you're targeting ES5, you may replace the let by var)

How can I detect the background color of a webpage with PhantomJS?

I'm using PhantomJS to rasterize some web pages with the render() function, and sometimes the resulting image has a black background (this is supposed to be a normal behaviour, as said at the bottom of the FAQ).
But I'd like to set a white background only if the web page has not set its background color, like this :
page.evaluate(function() {
if (getComputedStyle(document.body, null).backgroundColor === 'transparent') {
document.body.bgColor = 'white';
}
});
But it doesn't work at all. I also tried with :
=== 'rgba(0, 0, 0, 0)'
to no avail.
What am I missing here ?
Be aware that some pages can set background color on root html tag. Example is here:
http://helpdesk.symphonical.com/hc/en-us
Setting 'body' background color will override color from 'html' tag.
I ended up setting color for root html tag, this way even if body has other color it will hide our custom color from html in page.evaluate:
// test the top-level HTML tag since it can also contain backgroundColor
// and we don't wont to override it in body
if (getComputedStyle(document.documentElement, null).backgroundColor === 'rgba(0, 0, 0, 0)') {
console.log('document.documentElement backgroundColor is transparent, setting color to white');
var style = document.createElement('style'),
text = document.createTextNode('body { background: #fff }');
style.setAttribute('type', 'text/css');
style.appendChild(text);
document.documentElement.insertBefore(style, document.documentElement.firstChild);
}
You could just append a style element with the lowest useful specificity (1) for body as the first element of head. The engine would automatically use the background which came later. In case none came later, this one would be used.
page.evaluate(function(){
var s = document.createElement('style');
s.setAttribute("type", "text/css");
s.innerText = "body { background-color: white; }";
// assuming head exists and is not empty:
document.head.insertBefore(s, document.head.firstChild);
});
You could also try * { background-color: white; } with a specificity of 0.
I think I found the problem. Actually :
document.body.bgColor = 'white'
doesn't work. I've used instead :
document.body.style.backgroundColor = '#ffffff'
and now I got the white background.

What is different with window and div widths between firefox and IE

I have a web page that uses a scrolling div to display table information. When the window is resized (and also on page load), the display is centered and the div's scrollbar positioned to the right of the page by setting its width. For some reason, the behaviour is different under firefox than IE. IE positions/sizes the div as expected, but firefox seems to make it too wide, such that the scrollbar begins to disappear when the window client width reaches about 800px. I'm using the following methods to set the position and size:
function getWindowWidth() {
var windowWidth = 0;
if (typeof(window.innerWidth) == 'number') {
windowWidth=window.innerWidth;
}
else {
if (document.documentElement && document.documentElement.clientWidth) {
windowWidth=document.documentElement.clientWidth ;
}
else {
if (document.body && document.body.clientWidth) {
windowWidth=document.body.clientWidth;
}
}
}
return windowWidth;
}
function findLPos(obj) {
var curleft = 0;
if (obj.offsetParent) {
curleft = obj.offsetLeft
while (obj = obj.offsetParent) {
curleft += obj.offsetLeft
}
}
return curleft;
}
var bdydiv;
var coldiv;
document.body.style.overflow="hidden";
window.onload=resizeDivs;
window.onresize=resizeDivs;
function resizeDivs(){
bdydiv=document.getElementById('bdydiv');
coldiv=document.getElementById('coldiv');
var winWdth=getWindowWidth();
var rghtMarg = 0;
var colHdrTbl=document.getElementById('colHdrTbl');
rghtMarg = parseInt((winWdth - 766) / 2) - 8;
rghtMarg = (rghtMarg > 0 ? rghtMarg : 0);
coldiv.style.paddingLeft = rghtMarg + "px";
bdydiv.style.paddingLeft = rghtMarg + "px";
var bdydivLft=findLPos(bdydiv);
if ((winWdth - bdydivLft) >= 1){
bdydiv.style.width = winWdth - bdydivLft;
coldiv.style.width = bdydiv.style.width;
}
syncScroll();
}
function syncScroll(){
if(coldiv.scrollLeft>=0){
coldiv.scrollLeft=bdydiv.scrollLeft;
}
}
Note that I've cut out other code which sets height, and other non-relevant parts. The full page can be seen here. If you go to the link in both IE and firefox, resize width until "800" is displayed in the green box top-right, and resize height until the scrollbar at the right is enabled, you can see the problem. If you then resize the IE width, the scrollbar stays, but if you resize the firefox width wider, the scrollbar begins to disappear. I'm at a loss as to why this is happening....
Note that AFAIK, getWindowWidth() should be cross-browser-compatible, but I'm not so sure about findLPos().... perhaps there's an extra object in Firefox's DOM or something, which is changing the result??
You are dealing with "one of the best-known software bugs in a popular implementation of Cascading Style Sheets (CSS)" according to Wikipedia. I recommend the Element dimensions and CSS Object Model View pages on Quirksmode.org.
Also: I think you'll find that Safari and Opera behave like Firefox in most circumstances. A more compatible approach to working around these problems is testing for, and making exceptions for, MSIE instead of the other way around.
Ok, I found the problem. Seems to be that firefox does not include the style.paddingLeft value in its style.width setting, whereas IE does, thus the div was ending up exactly style.paddingLeft too wide. That is, if for example style.paddingLeft is 8, IE's style.width value would be 8 more than FireFox's - and thus the inverse when setting the value, for FireFox I needed to subtract the style.paddingLeft value
Modified code with:
if (__isFireFox){
bdydiv.style.width = winWdth - bdydivLft - rghtMarg;
} else {
bdydiv.style.width = winWdth - bdydivLft;
}
As long as you don't include a valid doctype, you can't expect consistent results, due to Quirks Mode. Go add one (HTML 4.01 Transitional is fine), then let us know if it still occurs.
Also see http://en.wikipedia.org/wiki/Quirks_mode.
In your getWindowWidth() function, whenever you grab the width of something, instead of this:
windowWidth = document.documentElement.clientWidth;
try this
windowWidth = Math.max(document.documentElement.scrollWidth, document.documentElement.clientWidth);
A detail to help optimize some of your code:
function getPos(elm) {//jumper
for(var zx=zy=0;elm!=null;zx+=elm.offsetLeft,zy+=elm.offsetTop,elm=elm.offsetParent);
return {x:zx,y:zy}
}
(jumper is a user who posted this code in Eksperten.dk)

Categories

Resources