Does CSS change the DOM? - css

I was wondering if CSS changes the DOM.
The reason I am asking, is that whenever I change an Element with CSS, I don't see it's value changed in the "element".style properties.

No, CSS does not change the DOM.

No. CSS does not change the DOM.
Nor content injected using :after or :before alter the DOM.

Actually... there are a few cases where CSS can change the DOM, but it's a bit far-stretched, as it won't change the DOM-tree structure, except in one yet even more far stretched case...
There is a being rendered definition in the HTML specs that does impact the behavior of the DOM in some cases, based on CSS computed styles.
For instance,
an HTMLImageElement can have its width and height IDL attributes value change whether it is being rendered or not:
onload = (evt) => {
console.log( 'rendered', document.getElementById( 'disp' ).width );
console.log( 'not rendered', document.getElementById( 'no-disp' ).width );
}
img { width: 100px; }
#no-disp { display: none; }
<img id="disp" src="https://upload.wikimedia.org/wikipedia/commons/4/47/PNG_transparency_demonstration_1.png">
<img id="no-disp" src="https://upload.wikimedia.org/wikipedia/commons/4/47/PNG_transparency_demonstration_1.png">
Elements that are not being rendered can not be focusable elements:
document.getElementById( 'rendered' ).focus();
console.log( document.activeElement ); // <input id="rendered">
document.getElementById( 'rendered' ).blur();
document.getElementById( 'not-rendered' ).focus();
console.log( document.activeElement ); // <body>
#not-rendered {
display: none;
}
<input id="rendered">
<input id="not-rendered">
And the one case where the DOM tree is modified, concerns the DOM tree of an inner Document: When an <object> or an <embed> element has its style set to display:none, per specs, its inner Document should be reloaded:
Whenever one of the following conditions occur
[...]
the element changes from being rendered to not being rendered, or vice versa,
...the user agent must queue an element task on the DOM manipulation task source given the object element to run the following steps to (re)determine what the object element represents.
So this means that simply switching the being rendered state of such an <object> or <embed> element is supposed to reload entirely its inner Document, which means also its DOM tree.
Now, only Safari behaves like that, Firefox never implemented that behavior, and Chrome did recently change their to match FF's one, against the specs.
For Safari users, here is a fiddle demonstrating it.

Related

Testing contents of after CSS selector in protractor

In my HTML I have element such as below
HTML:
<hmtl>
<head>
<style>
label::after {
content: " *"
}
</style>
</head>
<body>
<label> I'm mandatory</label>
</body>
</hmtl>
So what gets displayed on browser is:
I'm mandatory *
Query Selector
>getComputedStyle(document.querySelector('label')).content
<"normal"
So I see normal instead of *.
I can't see where is normal coming from. Is this the correct way to test content of ::after CSS selector?
I want to test that there's a "*" after the label, but can't seem to be able to get the value of "content" property correctly. Once I'm able to find it in using browser DOM API, I'd eventually want to test it in protractor.
Update
I found the answer at - Selenium WebDriver get text from CSS property "content" on a ::before pseudo element.
Now the question remains how I would test this on protractor.
Window.getComputedStyle()
The Window.getComputedStyle() method returns an object containing the values of all CSS properties of an element, after applying active stylesheets and resolving any basic computation those values may contain. Individual CSS property values are accessed through APIs provided by the object, or by indexing with CSS property names.
Syntax:
var style = window.getComputedStyle(element [, pseudoElt]);
element
The Element for which to get the computed style.
pseudoElt (Optional)
A string specifying the pseudo-element to match. Omitted (or null) for real elements.
The returned style is a live CSSStyleDeclaration object, which updates automatically when the element's styles are changed.
You can find a related discussion in WebDriver select element that has ::before
Usage with pseudo-elements
getComputedStyle() can pull style info from pseudo-elements (such as ::after, ::before, ::marker, ::line-marker.
As per the HTML, the <style> is as follows:
<style>
label::after {
content: " *"
}
</style>
Implemented as:
<label> I'm mandatory</label>
To retrieve you need to:
var label = document.querySelector('label');
var result = getComputedStyle(label, ':after').content;
console.log('the generated content is: ', result); // returns ' *'
Reference
CSS Pseudo-Elements Module Level 4
const label = document.querySelector('label'); // "normal";
console.log(label);
const labelAfter = getComputedStyle(label, ':after').content;
console.log(labelAfter == "normal");
label::after {
content: " *"
}
<label> I'm mandatory</label>
Since my question was specifically w.r.t protractor I'm posting the solution that I got working. Coming to the part I was stuck initially - why do I get "normal" instead of " *"
>getComputedStyle(document.querySelector('label')).content
<"normal"
So earlier I was unaware that ::after creates a pseudo child element inside the label element.
Inspecting <label> element in Chrome shows the below HTML
<label>
I'm mandatory
::after
</label>
If I click<label> element and checked the Computed tab, I could see that the value for content property is normal.
However, if I click on ::after pseudo-element, I can see in the Computed tab the value for content property is " *".
As mentioned in the other answers getComputedStyle() with the pseudo element as second parameter, is the only way to get value of CSS property for "::after". The crux of the problem is that protractor does not have an equivalent for getComputedStyle(), so we have to rely upon browser.executeScript() as shown below:
let labelHeader = 'I'm mandatory *';
// Passing label element separately as in the real test case, it would be extracted from parent
// enclosing element and need to be able to pass it as a parameter to browser.executeScript().
let label = element(by.css('label'));
browser.executeScript("return window.getComputedStyle(arguments[0], ':after').content",
label)
.then ((suffixData: string) => {
// suffixData comes out to be '" *"', double quotes as part of the string.
// So get rid of the first and last double quote character
suffixData = suffixData.slice(1, suffixData.length - 1);
labelText += suffixData;
expect(labelText).toBe(labelHeader);
});

:empty doesn't work if there's blank spaces?

Trying to find a pseudo class that'll target a <div> like this:
<div class="nav-previous">
</div>
I've tried :blank and :empty but neither can detect it. Is it just not possible to do?
https://jsfiddle.net/q3o1y74k/3/
:empty alone is enough.
By the current Selectors Level 4 specification, :empty can match elements that only contain text nodes that only contain whitespace as well as completely empty ones. It’s just there aren’t many that support it as per the current specification.
The :empty pseudo-class represents an element that has no children except, optionally, document white space characters.
From the MDN:
Note: In Selectors Level 4, the :empty pseudo-class was changed to act like :-moz-only-whitespace, but no browser currently supports this yet.
The :-moz-only-whitespace CSS pseudo-class matches elements that only contain text nodes that only contain whitespace. (This includes elements with empty text nodes and elements with no child nodes.)
As the others mentioned, this isn't possible with CSS yet.
You can check to see if there's only whitespace with JavaScript however. Here's a simple JS only solution, "empty" divs that match are blue, while divs that have text are red. Updated to add an empty class to the empty divs, which would allow you to target them easily with the selector .empty in your CSS.
The JS only "empty" comparison would look like this:
if(element.innerHTML.replace(/^\s*/, "").replace(/\s*$/, "") == "")
And if you're using jQuery it would be a bit easier:
if( $.trim( $(element).text() ) == "" ){
var navs = document.querySelectorAll(".nav-previous");
for( i=0; i < navs.length; i++ ){
if(navs[i].innerHTML.replace(/^\s*/, "").replace(/\s*$/, "") == "") {
navs[i].style.background = 'blue';
navs[i].classList.add( 'empty' );
} else {
navs[i].style.background = 'red';
}
}
.nav-previous {
padding: 10px;
border: 1px solid #000;
}
.nav-previous.empty {
border: 5px solid green;
}
<div class="nav-previous">
</div>
<div class="nav-previous">Not Empty </div>
The problem with your approach is that your container is not actually empty.
The :empty pseudo-class represents an element that has no children at
all. In terms of the document tree, only element nodes and content
nodes (such as DOM text nodes, CDATA nodes, and entity references)
whose data has a non-zero length must be considered as affecting
emptiness;
As you have empty spaces this pseudo class will not do the trick.
The :blank pseudo class should be the right one, because this is its definition:
This blank pseudo-class matches elements that only contain content
which consists of whitespace but are not empty.
the problem is that this pseudo class isn't implemented by any browser yet as you can check in the link below. So you will need to wait until it get implemented to be able to use this selector.
This pretty much explains the behavior you are facing
https://css4-selectors.com/selector/css4/blank-pseudo-class/
The best approach here is just to be sure that your div will actually be empty, so your approach will work.
the best that you can do is to define an empty class like this:
.empty{
display:none;
}
and then add this JS code here, it will append the empty class to your blank items:
(function($){
$.isBlank = function(html, obj){
return $.trim(html) === "" || obj.length == 0;
};
$('div').each(function() {
if($.isBlank(
$(this).html(),
$(this).contents().filter(function() {
return (this.nodeType !== Node.COMMENT_NODE);
})
)) {
$(this).addClass('empty');
}
});
})(jQuery);
check it working here,
https://jsfiddle.net/29eup5uw/
You just can't without JavaScript/jQuery implementation.
:empty selector works with empty tags (so without even any space in them) or with self-closing tags like <input />.
Reference: https://www.w3schools.com/cssref/css_selectors.asp
If you want to use JavaScript implementation, I guess here you will find the answer: How do I check if an HTML element is empty using jQuery?
:empty indeed only works for totally empty elements. Whitespace content means it is not empty, a single space or linebreak is already enough. Only HTML comments are considered to be 'no content'.
For more info see here: https://css-tricks.com/almanac/selectors/e/empty/
The :blank selector is in the works, it will match whitespace, see here: https://css-tricks.com/almanac/selectors/b/blank/. But it seems to have no browser support yet.
Update:
See here for possible solutions to this involving jQuery.

how to protect embedded div style not to be overridden by website style

I have div with its own style. I embedded this div on other website.
<div id="scoped-div">
<style>
label {
color: green;
}
</style>
<label> Scoped div </label>
</div>
But I face problem, my div style is overridden by website style. I don't want to use iframe. Except for the use of iframe is there any other way to protect my div style by external style changes?
Your request is exactly what Shadow DOM makes possible:
attach a Shadow DOM to the element you want to protect (here:
#scope-div),
put the HTML code you want to protect in the Shadow DOM,
clone it from a <template> element to get it easy (optional).
That's it!
var div = document.querySelector( "#scoped-div" )
var template = document.querySelector( "template" )
var sh
if ( 'attachShadow' in div )
sh = div.attachShadow( { mode: "closed" } ) //Shadow DOM v1
else
sh = div.createShadowRoot() //Shadow DOM v0 fallback
sh.appendChild( template.content.cloneNode( true ) )
<template>
<style>
label {
color: green;
}
</style>
<label> Scoped div </label>
</template>
<div id="scoped-div">
</div>
There is no way to fully protect your styles. But you can try the following:
Try to specify your elements selectors as specific as possible (e.g. with attributes and IDs)
Use inline styles
Use !important (but be careful with a broad use of importants)

CSS: What does "input[type="search"]::-webkit-search-decoration" do?

I wonder what the the part ::-webkit-search-decoration do in the CSS selector for input[type="search"]::-webkit-search-decoration?
And why is this causing en DOM Exception error?
function is(selector, element) {
var div = document.createElement("div"),
matchesSelector = div.webkitMatchesSelector;
return typeof selector == "string" ? matchesSelector.call(element, selector) : selector === element;
}
is('input[type="search"]::-webkit-search-decoration', document.body);
It allows you to make search boxes look uniform across multiple browsers. Chrome for instance has default styling for search boxes that does not fit into some designs.
here is a good link on the topic.
http://geek.michaelgrace.org/2011/06/webkit-search-input-styling/
It just makes your search box little bit styled.As it is one of the property for css3 then it will not work on every browser.
Have a look in this link
http://css-tricks.com/webkit-html5-search-inputs/

How to know if element height or width was set in javascript/css?

Is there any way to know if an element height or width was set (not auto) in javascript/css ?
elm.style.height will only return a value if the height is defined inside the element attribute list : <div style='height:200px' .... ></div>, otherwise it will always return an empty string even if you define the height inside a style tag or a css file : .myElmCss{height:200px}.
On the other hand, using window.getComputedStyle() or elm.currentStyle will always return a value even if no height was defined neither inside the element attribute list nor in a css file/style tag.
Thanks.
Check this post How do you read CSS rule values with JavaScript?
To do what you're looking for it appears to be a matter of iterating over the stylesheets to find declared properties. You would probably also cross reference with inline styles like you mentioned in your question.
from #InsDel's post:
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);
}
}
}

Resources