Make inserted DOM node not affected by CSS - javascript

I'm trying to develop a Firefox addon/Chrome extension. How to insert a new DOM node to arbitrary place of arbitrary page without being affected by its CSS?
Take a look at this demo: http://jsfiddle.net/za5cop0e/1/. If the web page modify the style of all div under certain node in some way (display: none as example), and I inserted my div to it without knowing that display is modified, my div will not behave as I expect.
One solution I can think of is to override all standard CSS styles with default value, which is unacceptable. Is there more elegant way to do this, for example, document.createElement('div', false), where the second argument indicates to the browser that this node does not inherit any CSS style from ancestors.

You can embed your own css to the DOM element that you are appending.
So for the DOM element you can add css for just that DOM that will override any other as inline CSS preceeds others
http://jsfiddle.net/za5cop0e/4/
var s = document.createElement('style')
s.textContent = 'div { display: none }'; // problematic CSS
document.head.appendChild(s);
// expect to show a black block, but will not show due to the CSS style above
var d = document.createElement('div');
d.style.backgroundColor = 'black';
d.style.width = '100px';
d.style.height = '100px';
d.style.display = 'block'; // here
document.body.appendChild(d);

Related

Ignore/override surrounding / inherited CSS

I have this call to add HTML/CSS to an existing page:
let div = document.createElement('div');
div.style.zIndex = 9999999;
div.innerHTML = str; // some pre-defined HTML string
document.body.insertBefore(div, document.body.firstChild);
I am creating a Chrome Extension that helps developers. What's happening is that the above HTML is inheriting the existing CSS from developer's pages. I want the styling of the above HTML to be independent from the CSS/styling on the developer's page.
Is there a way to ignore all existing CSS on page? I'd like to basically create a "CSS sandbox".
I think one way to create such a sandbox, would be an iframe, but I am looking for a simpler way to do that.
Add a class to elements which you want to reset..and then apply all:unset to that class
The all CSS shorthand property sets all of an element's properties (apart from unicode-bidi and direction) to their initial or inherited values, or to the values specified in another style sheet origin.
...all:unset
Specifies that all the element's properties should be changed to their inherited values if they inherit by default, or to their initial values if not.(It will ignore all the user agent style too.)
Stack Snippet
let p = document.createElement('p');
p.style.color = "red";
p.innerHTML = "Hello"; // some pre-defined HTML string
p.classList.add("reset");
document.body.insertBefore(p, document.body.firstChild);
p {
background: black;
}
.reset {
all: unset;
}

Create a very low specificity css property

I am trying to create a very low specificity css property using javascript. Just like !unimportant (which doesn't exists)
I don't know whether this is possible or not.
My reason to look for something like !unimportant is that I am writing a small javascript plugin. In which I want to add a default style to a element which should be later easily overriden by the user.
But if I write:
element.style.backgroundColor = "green";
The user will not be able to override the above style easily without using !important. So, I added a dynamic style tag by using the following code:
var style = document.createElement('style');
// WebKit hack :(
style.appendChild(document.createTextNode(""));
document.head.appendChild(style);
and then to the above code, I added a dynamic stylesheet using the following code:
var element = document.getElementById('main');
// To use attribute names to apply the styles
element.setAttribute('custom-el', '1');
var sheet = style.sheet;
var properties = "background-color: green;";
var elName = "[custom-el]";
if (sheet.insertRule) {
sheet.insertRule(elName + "{" + properties + "}", 0);
} else if (sheet.addRule) {
sheet.addRule(elName, properties, 0);
}
Now the background-color: green can be overriden by using the following code:
div.main {
background-color: red;
}
But as you can see in css, I used higher specificity to override background-color: green i.e div + .green.
But I want the overriden to happen even when user writes the following css:
.main{ /* Could be simple class name or id name or even tag name */
background-color: red;
}
Fiddle
This might seems to be a small issue. but it is a big problem for me. Please help.
I would simply write like this:
element.style.backgroundColor = element.style.backgroundColor || "green";
Where, if backgroundColor is undefined then it uses green as backgroundColor else it would take the backgroundColor from stylesheet.
Finally I got the answer..
document.head.insertBefore(style, document.head.children[0]);
I should just insert the dynamic stylesheet above already present stylesheets in the head tag.
Working Fiddle
Unfortunately, this is not working in any IE version. I am still looking for answer.

Setting inline css transition style with string alters the true value. Bizarre

I have a stylesheet which sets a css transition property like so (prefixed versions omitted for brevity):
transition: opacity 1s;
Then I have a number of elements on the page, and I wish to modify the transition-delay property of each element via JavaScript, to give a stagger effect. I am using jQuery like so:
$(element).css('transition-delay', delay + 's');
However, the above code does not add an inline transition-delay: Xs to the element. Instead, it results in:
<div style="transition: Xs;">
But that's fine, because it works as expected. Somehow, the browser knows that transition: Xs really means to just set the transition-delay to Xs and leave the rest intact.
However:
If I now get the inline style of that element via $(element).attr('style'), and then re-apply it to the element, $(element).attr('style', style), the HTML looks exactly the same, but now the transition has totally overwritten the other properties and essentially sets the element's transition value to all Xs ease 0s.
// HTML before - working
<div style="transition: Xs">
// then I do this
var style = $(el).attr('style');
$(el).attr('style', style);
// HTML after - broken!
<div style="transition: Xs">
Demo
A JSFiddle of exactly what I have described: http://jsfiddle.net/7vp8m/4/
What is going on?
I think just writing out the question and coding that demo really helped me to find the answer:
The HTML style attribute is not the actual style. We need to use the CSSStyleDeclaration object
Although it seems that the inline style is as simple as whatever is contained in the style="..." HTML attribute (as I had assumed), it turns out that this is not the case. Behind the scenes, inline styles (and all other styles) are actually defined by an object called CSSStyleDeclaration. The string contained in the style attribute only represents this object, but does not contain all the information needed to define a style.
This is why setting `el.style = "width: 100px;" does not work. From the MDN article on HTMLElement.style:
Except in Opera, styles can not be set by assigning a string to the (read only) style property, as in elt.style = "color: blue;". This is because the style attribute returns a CSSStyleDeclaration object. Instead, you can set style properties like this:
elt.style.color = "blue"; // Directly
var st = elt.style;
st.color = "blue"; // Indirectly
So this shows us why doing $(el).attr('style', 'transition: Xs'); will not work as expected - and this is exactly what I was running into. Doing so will modify the underlying CSSStyleDeclaration object, but not always in the way we want it to (hence my original question).
The solution is therefore to use the API provided by CSSStyleDeclaration. The following SO question proved crucial to me understanding this issue: JavaScript & copy style
Copying a CSSStyleDeclaration:
var originalStyle = el.cloneNode().style;
Here we are using the cloneNode() method because otherwise (if we just get el.style) the CSSStyleDeclaration object is copied by reference, which is not what I want since I will be changing the element's inline style and then I want to restore the original style later. Cloning the element first allows us to get a "fresh" copy of the CSSStleDeclaration that will not change when we alter the inline styles of our element el.
Replacing current inline style with the saved CSSStyleDeclaration
// first we need to delete all the style rules
// currently defined on the element
for (var i = el.style.length; i > 0; i--) {
var name = el.style[i];
el.style.removeProperty(name);
}
// now we loop through the original CSSStyleDeclaration
// object and set each property to its original value
for (var i = originalStyle.length; i > 0; i--) {
var name = originalStyle[i];
el.style.setProperty(name,
originalStyle.getPropertyValue(name),
priority = originalStyle.getPropertyPriority(name));
}
Demo
Here is an update of my original demo that implements the above methods: http://jsfiddle.net/7vp8m/11/
It breaks in chrome and the "new" opera but no in ff. In Maxthon it stops and restarts the animation the first time, then it works well.
As you said in http://jsfiddle.net/7vp8m/5 (fortunately you solved it) it is due to setting the transition delays through a inline style.
But if you force the engine to refresh the css it works somehow (stops the animation at first but then it continues, playing the animation slower): http://jsfiddle.net/7vp8m/7/
function tick() {
[...]
$.each($('.test'), function(i, e){
e.style.marginLeft = x + 'px'; // Trying with vanilla js but it is the same
e.offsetHeight; // force the refresh. It moves again but bad
});
[...]
}
This doesn't work too: http://jsfiddle.net/7vp8m/8/
$.each($('.test'), function (index, el) {
var style = $(el).attr('style');
style += '; transition-delay: '+delay + 's;'+
'-webkit-transition-delay'+delay + 's;'
$(el).attr('style', style);
delay += 0.2;
});
It seems to be a webkit bug related to transition-delay, but Maxthon stopped the animation in a similar way so it probably is a more generalized bug.
So, if it is a bug, the best option is to not use the property transition-delay through js.

How to make a DIV visible and invisible with JavaScript?

Can you do something like
function showDiv()
{
[DIV].visible = true;
//or something
}
if [DIV] is an element then
[DIV].style.visibility='visible'
OR
[DIV].style.visibility='hidden'
Let's assume you do not use a library such as jQuery.
If you do not already have a reference to the DOM element, get one using var elem = document.getElementById('id');
Then you can set any CSS property of that element. To show/hide, you can use two properties: display and visibility, which have slightly different effects:
Adjusting style.display will look as if element is not present at all ("removed").
elem.style.display = 'none'; // hide
elem.style.display = 'block'; // show - use this for block elements (div, p)
elem.style.display = 'inline'; // show - use this for inline elements (span, a)
or style.visibility will actually make the div still be there, but be "all empty" or "all white"
elem.style.visibility = 'hidden'; // hide, but lets the element keep its size
elem.style.visibility = 'visible';
If you are using jQuery, you can do it even easier as long as you want to set the display property:
$(elem).hide();
$(elem).show();
It will automatically use the appropriate display value; you do not have to care about the element type (inline or block). Additionally, elem can not only be a DOM element but also a selector such as #id or .class or anything else that is valid CSS3 (and more!).
You can use visibility or display but you have to apply changes to the div.style object and not the div object itself.
var div = document.getElementById('div_id');
// hide
div.style.visibility = 'hidden';
// OR
div.style.display = 'none';
// show
div.style.visibility = 'visible';
// OR
div.style.display = 'block';
You can use the DOM functions: setAttribute and removeAttribute.
In the following link you have an example of how to use them.
setAttribute and removeAttribute functions
A quick view:
hide: document.getElementById("myDiv").setAttribute("hidden","");
unhide: document.getElementById("myDiv").removeAttribute("hidden");
as of November 2022 browser support for CSS revert value is 94.56% (https://caniuse.com/?search=revert) so if for hiding you use
elem.style.display = 'none'; // hide
for visibility use
elem.style.display = 'revert'; // show
this posolite doesn't care about element type
Note: The revert keyword is different from and should not be confused
with the initial keyword, which uses the initial value defined on a
per-property basis by the CSS specifications. In contrast, user-agent
stylesheets set default values on the basis of CSS selectors.
For example, the initial value for the display property is inline,
whereas a normal user-agent stylesheet sets the default display value
of <div>s to block, of <table>s to table, etc.
revert
You can use opacity which is similar to visibility but allow to smooth transition and control other parameters like height (for snippet simplicity I put js logic in html directly - don't do it in production code)
.box { width:150px; height: 150px; background: red; transition: 0.5s }
.hide { opacity: 0; height: 10px}
<div id="box" class="box"></div>
<button onclick="box.classList.toggle('hide')">Toggle</button>
Make Invisible using CSS
#div_id {
/*height: 400px;*/
visibility:hidden;
}
Make Visible using Javascript
document.getElementById('div_id').style.visibility = 'visible';
Use 'hidden' attribute of DOM element:
function showDiv(isVisible)
{
[DIV].hidden = !isVisible;
}
ID is the name of your div. Make sure to have runat="server" in the Div.
document.getElementById('<%= ID.ClientID %>').hidden = false;
document.getElementById('<%= ID.ClientID %>').hidden = true;

Dynamically add css to page via javascript

I'm making a widget that will be added to external websites, and I have made a page that generates css for them to style it (text color, background color, font size, etc). I end up with a textarea filled with css for them to copy/paste to their website.
Is there a way to add this css to the current page in order to have a live preview?
If you want to add CSS as text
var style = document.createElement('style');
style.innerHTML = 'content';
document.head.appendChild(style);
If you want to add a CSS file
var link = document.createElement('link');
link.setAttribute('rel', 'stylesheet');
link.setAttribute('href', 'css/my.css');
document.head.appendChild(link);
I have traditionally appended a <style> block when doing elements.
var style_rules = [];
style_rules.push("#" + myElemId + " { /* Rules */ } ");
/* ... */
var style = '<style type="text/css">' + style_rules.join("\n") + "</style>";
$("head").append(style);
An important thing to note is that because you don't know what any of the existing styles is, or what id's might conflict on the page, it's very useful to keep track of your id's inside your JavaScript application, then using those to populate the injected <style> block. I also tend to run my names through a prefix function to ensure that the generic names of wrapper, and unit do not conflict (they are turned into something like myunique_wrapper and myunique_unit.
Incorporating a basic CSS reset like #myWrapper {margin: 0; padding: 0} can be a decent starting platform for building your own custom styles.
Addressing your unique case, a live preview so to speak, I would designate a div with standard elements. Then when they click "update" read in the rules and append them to the head. If you want to negate any residual effects from past rules you can remove the last <style> element or better yet give your <style> element an id. I'm not sure if that kind of selection would work, but it should.
var element = document.createElement('style');
element.setAttribute('type', 'text/css');
if ('textContent' in element) {
element.textContent = css;
} else {
element.styleSheet.cssText = css;
}
document.getElementsByTagName('head')[0].appendChild(element);
Can you add a style tag to the DOM, with the contents of the text-area in it? You may want to give it an id so you can change it later.
I recommend you start using a decent framework for your web/JavaScript development, personally I'd go with jQuery.
http://api.jquery.com/css/
There are some code snippets here that show you how to quickly set css properties for elements.

Categories