I am wondering if there is a way to get css value from stylesheet file when there is no element in the DOM using it? I am using jQuery and I use selector $(".classname").css() to get the values. But with the case "classname" is not in any element, what to do to get the value" Thanks
Just create an element with that class name and inspect it. You don't even need to attach it to the DOM:
var $el = $('<div class="classname"></div>');
var opacity = $el.css('opacity') // or whatever
Even though $el is not actually present in the DOM, you still get access to all of its style properties.
Edit: as mentioned in the comments, this approach does not always work as expected (eg inherited css values not defined on .classname explicitly, selector specificity above .classname, etc).
For example the following fails due to #foo increasing the selector specificity beyond that of a standalone .bar:
css:
#foo .bar { color: red; }
js:
var $el = $('<div class="bar"></div>');
$el.css('color'); // Expected: "red", Actual: ""
See http://www.hunlock.com/blogs/Totally_Pwn_CSS_with_Javascript which presents a cross-browser approach to getting and adding css dynamically.
It works with document.styleSheets and both IE's .rules and everyone else's .cssRules
It also has the advantage of being somewhat abstracted so you don't need to worry about the details.
The above link no longer works. The following is a screenshot of the blog-article, captured by the internet archive wayback in 2008.
The function basically iterates over all styles of all stylesheets and provides the ability to modify / delete them.
Please note that this cannot be recommended, since most modern stylesheets are too large to make this an efficient operation.
You could go (this also works in chrome):
var $elem = $('<div id="foo"></div>').appendTo('body'),
value = $elem.css('margin-top');
$('#foo').remove();
You have to parse the styles from document.styleSheets.
To workaround the Chromium problem, you need to add it to the body:
Sample CSS:
.dummy {
min-width: 50px;
max-width: 250px;
}
Sample Javascript:
$(document).ready(function() {
var dummy = $('<div class="dummy" style="display: none;"></div>').appendTo('body');
console.log(dummy.css("min-width"));
console.log(dummy.css("max-width"));
dummy.remove();
});
This works on all modern browsers, Chrome, FF, IE11.
Related
I am making a chrome extension, and I want to find all occurrences of a certain word, and put them inside a new div class so I can change them with CSS.
Right now my content.js file looks like this:
(function() {
function change() {
var words = new Array("color");
var html = document.body.innerHTML;
for(var i = 0; i < words.length; i++) {
var reg = new RegExp(words[i], 'ig');
var html = html.replace(reg, '<div class="colorClass">'+words[i]+'</div>');
}
document.body.innerHTML = html;
}
change();
})();
But when I do that, entire web pages get messed up and become unrecognizable (for example, the "Color" wikipedia page).
When I try something simpler like:
var html = html.replace(reg, '<i>'+words[i]+'</i>');
It works just fine. Why is this whole page getting messed up when I try to add divs?
How can I fix this?
One reason the styles are messed up are because the native web page's CSS is overriding the styles loaded from your content script.
The second reason is that you need to be very careful when modifying HTML. You need be careful only to modify text. The current method you are using - you are possibly modifying html classes, ids, etc.
I've actually built an extension (Source code for reference) that does something very similar. I'll share my learnings:
JS
I had used NodeIterator which is fairly unknown. I discovered this API a year ago from another extension: https://code.google.com/p/chrome-type-ahead/
NodeIterator will help you filter out the true text elements that you want to add styles to. This will help you avoid modifying any valid html(classes, ids, etc.) that matches your regex.
CSS
I would suggest adding a class to the html tag in addition to the html you are inserting. The reason for this is that you'll need to be very specific in your styles.
You want to avoid at all cost any chance of a collision with a selector of an existing page.
In addition you need to make sure to add your own css resets that are not only namespaced but also have !important on each attribute.
You CANNOT assume anything about the html tag that you are inserting. Sites can have very generic selectors that will completely mess up your styles. And also the sites can have very specific selectors (using id) that are more specific than you're styles (hence the need for important)
html.very-specific-class .very-specific-color-class {
/*
padding, margin, border, etc.
difficult to add all properties but add all the common ones at the very least
*/
padding: 0px !important;
margin: 0px !important;
border: 0px !important;
...
}
This is happening because a <div> is a block-level element, and it pushes other elements to a new line.
<i> however, is an inline element, so it will not change the page layout at all.
I would recommend using a <span>,instead of a <div>, as span's have no default styling in browsers.
Because divs are block elements. You can use spans, which are inline elements:
var html = html.replace(reg, '<span class="colorClass">'+words[i]+'</span>');
If you want use div,you can add some css styles,like this:
.colorClass{display:inline-block;}
I'm using the CSS content attribute to pass some values from my LESS stylesheet to JavaScript (to use some colors defined in LESS in Canvas elements).
To make my life easier I decided to place these values in a easy way to parse them in JavaScript.
LESS code:
div#colorChart-critical {
content:'#{critical-highest},#{critical-veryhigh},#{critical-high},#{critical-low},#{critical-medium},#{critical-verylow}';
}
which when compiled brings the following CSS:
div#colorChart-critical6 {
content: '#ff0000,#ff7200,#fffc00,#0000ff,#a200ff,#00ff00';
}
Then I try to read them using jQuery:
$("div#colorChart-critical").css("content").split(",");
The problem is that in IE9 calling $("div#colorChart-critical").css("content") is returning the string "normal" for some reason. Opera, Firefox, Safari and Chrome works fine.
Why does this happen in IE9?
Any work-around this issue on IE9? If not any other CSS atribute I can put random texts in?
I could use something like:
background: url(#ff0000,#ff7200,#fffc00,#0000ff,#a200ff,#00ff00);
But this would generate errors on the console.
It's because content as defined in CSS2.1 doesn't work on elements, only on the :before and :after pseudo-elements. IE9 is simply following the CSS2.1 spec here, which mandates that content on elements be computed to normal, always.
I don't know why other browsers would return the value you have defined, especially considering that .css() makes use of getComputedStyle() on those browsers. If they're implementing CSS2.1 content, then they're violating CSS2.1 by not computing the value to normal. If they're preparing for a late CSS3 implementation, whatever that may be, then it would make sense that they implement it on actual elements somehow... shame on them either way.
Which brings me to another point: if you're not actually trying to use CSS to modify the content of an element, don't use content, even if the fact that it's not defined for use with elements is the reason you're making use of this technique in the first place. You can try assigning those colors to certain classes, creating a hidden element and querying that element's color styles instead.
BoltClock answer shows the cause of my problems. I found a work-around by using the font-family instead of the content CSS property.
My LESS code:
div#colorChart-maincolors {
font-family: '#{colorChart1},#{colorChart2},#{colorChart3},#{colorChart4},#{colorChart5},#{colorChart6}';
}
Which compiled into CSS gives:
div#colorChart-maincolors {
font-family: '#c0392b,#2980b9,#2ecc71,#f1c40f,#ecf0f1,#34495e';
}
The string can be acquired using:
removeQuotes= function(string) {
return string.replace(/^['"]+|\s+|\\|(;\s?})+|['"]$/g, '');
};
removeQuotes($("#colorChart-maincolors").css("font-family")); //add a .split(',') to get the colors as an array
The function removeQuotes is necessary because each browser adds a different kind of quotes into the return of getComputedStyle (and by extension the jQuery .css() method). IE9 adds a double quote, Webkit adds a single quote.
See this post on CSS tricks: http://css-tricks.com/making-sass-talk-to-javascript-with-json/ for more information.
you can use replace(/["']/g, "") to remove extra quotation from string
""string"" will be change to "string"
I have the following javascript code:
$('#s2 a').click(function(){
var cB = $(this);
var f = parseInt(cB.attr('data-f'));
var r = parseInt(cB.attr('data-r'));
var c = parseInt(cB.attr('data-c'));
if (pA == false && !isClickAllowed(f,r,c)) {
return false;
}
// more stuff comes here
}
This makes a link not clickable. This all works. I also want to remove the hover effect. The CSS code for this is:
.pc a:hover {
background-color: #FFF;
I thought removing the class would do it like: cB.removeClass('pc'); but this doesn't work.
Any suggestions on how to do this?
Thank you for your time
--EDIT--
Hmm I think I see why it aint working. At the top of the document I have this:
$(document).ready(function() {
setScale();
$(window).resize(setScale);
if (!('ontouchstart' in document)) {
$('body').addClass('pc');
}
more code here
This sets the .pc a:hover for all links when opened the page on a pc rather then a touch device (e.g. iPad). I need to disable this pc hover ONLY on the links are not clickable like in:
if (pA == false && !isClickAllowed(f,r,c)) {
return false;
}
Hope this helps!
cB is the anchor which isn't being references by the CSS class you indicate, the parent would have the class pc for this to work. cB.parent().removeClass('pc'); would do it.
-- EDIT --
Following the erudite comments below it would seem that cb.parents('.pc').removeClass('pc') or cb.parents().removeClass('pc') (I've not benchmarked to see which is quicker) would be the comprehensive solution.
Good catch to James, Anthony, and Tadeck!
-- EDIT 2 --
Following the question update, I'd suggest adding another class to your links, i.e. clickable, then your CSS becomes:
.pc a.clickable:hover {
background-color: #FFF;
and you can just remove the clickable class on those links that you don't want to show the highlight. The better course of action may be to simply replace the links that are disabled with either raw text or as spans with an identifying class, i.e. disabled_link if you want to have the option to enable them later.
Though there was no HTML provided, based on the code, cB does not appear to be the element which has the class of pc but rather an ancestor of cB. You would need to remove the class from that.
If the direct parent is the only ancestor with the class of pc, you can do the following:
cB.parent().removeClass("pc")
If only one ancestor other than the direct parent has the class of pc and the parent does not, you can do the following:
cB.closest(".pc").removeClass("pc")
If multiple ancestors have the pc class, you can use the following:
cB.parents(".pc").removeClass("pc")
And finally, if multiple a tags exist within .pc then you cannot use the approach of removing the class, as this will affect all a tags within that .pc.
cB.removeClass('pc') should indeed remove aclass. Make sure your css behaves correctly in all scenarios. Also try targeting the parent
I would add a disabled class to the CSS that comes after the :hover rule and overrides it with the disabled styles, which may or may not be the default. Then, you can just do:
cB.addClass('disabled');
The reason your existing solution doesn't work is because you're removing the class from the element when its parent has the class.
Since your CSS issue was already solved, I want to point out another improvement:
I see that you are using cB.attr('data-f') to store some data, but attr should only be used for valid HTML attributes. You should consider using the jQuery data method which was created just for the purpose of storing non-attribute data into an element.
http://api.jquery.com/jQuery.data/
<p id="example">This is an example.</p>
<script>
document.getElementById('example').style.borderWidth = '4px';
</script>
Note that borderWidth is different than border-width. This can be used for the other styles. As a rule of thumb, take away the dash and make the first letter of the second word capital. If it doesn't work, Google it.
I have a parent div gal1, inside which there can be further divs and content, but only one img element without an id as you can see below. Now I want to use only Javascript (no jQuery) to change the style of this image, using gal1 as argument (because the rest of the structure inside this div may vary, only this one image will always be there somewhere). I couldn't find any other stackoverflow question that addresses exactly my situation.
<div class="gallery-preview" id="gal1">
<div class="gallery-preview-img" id="gal-img1">
<img src="galleries/kevin-carls/Monks.jpg">
</div>
<div class="gallery-preview-text" id="gal-text1">
<span class="gallery-name" href="">Road to India</span><br/>
<span class="artist-name" href="">Kevin Carls</span>
</div>
</div>
Than you can make use of method called getElementsByTagName('img') than you should get image and update it.
document.getElementById(gal1).getElementsByTagName('img');
get the content by using id, and then query images by using getElementsByTagName
function getImages(contentId) {
var content = document.getElementById(contentId);
// only one image, just return an item; or you can return an array
if (content) return document.getElementsByTagName('img')[0];
}
You can insert CSS which may be more efficient if you have to do this in more than this single case.
http://jsfiddle.net/65Ggv/
var style_rules = [];
style_rules.push("#gal1 img { border: 3px solid green; } ");
var style = style_rules.join("\n");
var el=document.createElement("style");
el.appendChild(document.createTextNode(style));
el.type="text/css";
document.head.appendChild(el);
Unless you absolutely need to pick the colors or border sizes dynamically, which I doubt because you are an admitted beginner, stuffing stylesheets in with Javascript is a Rube Goldberg device. It seems nifty to be able to do this, but if your application is important to you, you will regret it. (You might as well use innerHTML to stuff in a stylesheet in that case -- at least it will be faster than making DOM calls.)
Pranay Rana's answer to use getElementsByTagName is the best option if your constraints are actually stable (only one img). Obtain an element reference el, to gal1, using getElementById, then var myimg = el.getElementsByTagName("img") and you are almost done.
If you insist upon funking with the style nodes, you can stuff whatever properties you want into the style property of myimg. It becomes inline style.
Even so, you almost certainly do not need to stuff in novel rules, and changing inline style is often avoidable. It is more predictable and stable to modify the class attribute on myimg, and use a predefined set of style classes for the cases you need to handle. This will give a nice clean separation of the style from the script, and avoid both the in-lining of the style rules and run-time mutilation of the style tree by code injection.
When we run following script under Firefox ...
var d = $("<div class='test'></div>");
d.hide();
$("body").prepend(d);
d.show();
... and look to HTML, this inserted element will have style attribute:
style="display: block;"
Under Webkit, element will have:
style="display: none;"
This scenario in used in a JavaScript component, i develop. This component has a collection of HTML elements in it's internal variables, and inserts they to specified destination container.
Because inserted element having display-property initialized in style attribute, it overrides CSS. This breaks the layout of a page.
As a quick solution, i can store "style" attribute, before element is inserted to the DOM, and, after inserting, write stored version over created one.
Is there any better solution ?
Why this ever happens and how can i check, whether element is not yet inserted to the DOM ?
When I do that with either Chrome or Safari (both WebKit-based browsers), if I inspect the element with the built-in tools, it has no style.display property at all, and so the default div style of display: block is used. (Here's a version with some text in the div, so it's easier to see and to find with the DOM inspector.)
So I suspect the problem lies elsewhere. For instance, is there intervening code that may be failing on WebKit, so that d.show(); never ends up getting called? That would certainly explain it. Easy enough with the built-in tools in Chrome or Safari to set a breakpoint on the code creating the div and walk through it.
Re your question:
...how can i check, whether element is not yet inserted to the DOM ?
That question was asked here on StackOverflow just recently, and one of the answers specific to jQuery was rather elegant:
if (d.closest("body").length == 0) {
// It's not in the DOM yet
}
Update: Re your comment below
Look at this test page with Firefox. The div has "style=display: block;" explicitly defined. Under Webkit, it has empty style attr. I'm using built-in inspector in both Firefox and Safari.
Ah, okay, so the problem isn't a display: none in WebKit browsers (your statement about that in the question led me astray), but rather that Firefox (and possibly other Gecko browsers) end up having display: block on the element.
I'd probably approach this like this:
var d = $("<div class='test'></div>");
d.addClass("hidden");
$("body").prepend(d);
d.removeClass("hidden");
...with this CSS:
.hidden {
display: none;
}
Live copy
That way, you know you won't end up with a style.display property set at all.
Update 2: The other thing you can do is remove the style.display property directly:
var d = $("<div class='test'>Hi there</div>");
d.hide();
$("body").prepend(d);
d.show();
d[0].style.display = "";
Live example
You alluded to effects, so if you're doing fadeIn or something like that, use the callback:
var d = $("<div class='test'>Hi there</div>");
d.hide();
$("body").prepend(d);
d.fadeIn(function() {
this.style.display = "";
});
Live example
How about if you do
var d = $("<div class='test'></div>");
d.hide();
$("body").prepend(d);
d.removeAttr('style'); // this will revert to the original styling