Is there a way to check if the CSS function calc is available using JavaScript?
I found lot of questions and articles about getting the same behaviour as calc using jQuery, but how can I only check if it's available?
In Modernizr you can find the test as css-calc currently in the non-core detects. They use the following code:
Modernizr.addTest('csscalc', function() {
var prop = 'width:';
var value = 'calc(10px);';
var el = document.createElement('div');
el.style.cssText = prop + Modernizr._prefixes.join(value + prop);
return !!el.style.length;
});
A bit late to the party but I figured I should share this because I was myself struggling with it. Came up with the idea of by using jQuery, I can create a div that uses the calc() value in a CSS property (such as width in this case) and also a fallback width in case the browser does not support calc(). Now to check whether it supports it or not, this is what I did:
Let's create the CSS style for the currently "non-existing" div element.
/* CSS3 calc() fallback */
#css3-calc {
width: 10px;
width: calc(10px + 10px);
display: none;
}
Now, if the browser does not support calc(), the element would return a width value of 10. If it does support it, it would return with 20. Doesn't matter what the values are exactly, but as long as the two width properties have different values in the end (in this case 10 and 20).
Now for the actual script. It's pretty simple and I suppose it's possible with regular JavaScript too, but here's the jQuery script:
// CSS3 calc() fallback (for unsupported browsers)
$('body').append('<div id="css3-calc"></div>');
if( $('#css3-calc').width() == 10) {
// Put fallback code here!
}
$('#css3-calc').remove(); // Remove the test element
Alternatively, native javascript check, and width:
#css3-calc { width: 1px; width: calc(1px + 1px); }
if( document.getElementById('css3-calc').clientWidth==1 ){
// clientHeight if you need height
/* ... */
}
Calc detection was added to modernizer according to their news page.
http://modernizr.com/news/
As well as tightening up support for existing tests, we've also added
a number of new detects, many submitted by the community:
[...]
css-calc
var cssCheck = document.createElement("DIV");
cssCheck.style.marginLeft = "calc(1px)";
if (cssCheck.style.getPropertyValue("margin-left"))
{
alert("calc is supported");
}
cssCheck = null;
Related
I have simple question about asigning values of CSS properties to some variables is JavaScript in Polymer app.
Assume I have one div with width:200px;. In some JavaScript function i want to change width to 200px+10px.
I know i can apply this in JS in this way div.style.top = '210px';, but this is not what I need!
I want to changing this width property, and have full control about this.
So I readed i can make some custom CSS variable to save my width:
:host {
--my-width: 200px;
}
div{
width: var(--my-width);
}
This is nice because now I have one CSS variable, and I can set this attribute to few selectors, elements.
The question is - how to get this variable in JS and change it in that way (pseudocode):
--my-width = --my-width + 10px
I know i can use this
this.updateStyles({
'--my-width': '210px'
});
to replace value, but I want to code something like this:
this.updateStyles({
'--my-width': '--my-width'.value + 10px
});
So that I could changing this width by adding some values (+10px) , not defining new (= 210px)
I'm asking about how to make this and about some good practices in polymer, how to do that.
You can use window.getComputedStyle and getPropertyValue:
const styles = window.getComputedStyle(this);
const myWidth = styles.getPropertyValue('--my-width');
const newWidth = `${parseFloat(myWidth) + 10}px`;
this.updateStyles({ '--my-width': newWidth });
It's worth reading the Polymer docs on custom properties. Although I'm not sure they're 100% up-to-date, they have some useful information re: Shady DOM.
An alternative, depending on your use case and the browsers you're targeting, is the CSS calc() function:
div {
width: calc(var(--my-width) + 10px);
}
You could do the same with updateStyles, of course.
I am trying to grab the HTML from a CSS truncated element and can't seem to get it right.
For example:
<span id=mySpan style=white-space:nowrap;overflow:hidden;text-overflow:ellipsis;width:50px>This is the contents of the span tag. It should truncate with an ellipsis if it is longer than 50px.</span>
If I use the standard jQuery way to grab the HTML, I get the full text, not the truncated version. I'm not sure if it is even possible.
html = jQuery('#mySpan').html();
text = jQuery('#mySpan').text();
Both return the full text. I'm stumped.
You can compute it :
$.fn.renderedText = function(){
var o = s = this.text();
while (s.length && (this[0].scrollWidth>this.innerWidth())){
s = s.slice(0,-1);
this.text(s+"…");
}
this.text(o);
return s;
}
var renderedText = $("#mySpan").renderedText(); // this is your visible string
Demonstration
Of course this only works for an element with overflow:hidden;text-overflow:ellipsis but it's easy to adapt when there's no text-overflow:ellipsis: just remove the +"…".
Note that this is compatible with all browsers and gives the exact result (the w3.org specifies that the … character is to be used by the browser).
#dystroy has given a nice answer, here is another (more future-friendly) way to do this though.
We can use document.caretPositionFromPoint. This is almost a FF only function, but most other browsers provide the same thing under their own function name and API. No I don't know what browsers have against devs but oh well...
Our method works like this:
select element
get bounding client position
put it in the above function to get text offset position
subtract 3 from it to remove the ellipsis thingy from the offset
extract text according to that offset from textContent property
Here is a quick demo (should work properly in Webkit and Gecko):
function getRenderedText (el) {
var pos = el.getBoundingClientRect();
var offset, range;
if (document.caretRangeFromPoint) {
range = document.caretRangeFromPoint(pos.right, pos.top);
offset = range.endOffset - 3;
}
else if (document.caretPositionFromPoint) {
range = document.caretPositionFromPoint(pos.right, pos.top);
offset = range.offset - 3;
}
else {
console.error('Your browser is not supported yet :(');
}
return el.textContent.slice(0, offset);
}
console.log(getRenderedText(el));
span {
text-overflow: ellipsis;
width: 40px;
white-space: nowrap;
display: block;
overflow: hidden;
}
<span id="el">foo bar is so much awesome it is almost the bestest thing in the world. devs love foo bar. foo bar all the things!</span>
I have seen an error of maximum 1 character in some cases (weird fonts or edge cases), but most of the time, it works fine.
Hope that helps!
What should the best practices to listen on element resize event?
I want to re-position an element (jQuery dialog in my case), once it's size changed. But I am now more interested to do in a general way to to listen to resize event, unaware of how the resize happens. It suppose to be simple until I found an element can be re-sized by
window resize
content text changes
children elements or their children elements resized
a sibling element resize (e.g. a cell in a table)
JavaScript changes it src(of img)/style attribute directly (or it's child's)
JavaScript rewrite CSS rules or stylesheet
native resize feature textarea or CSS3 resize
browser's zoom or text-enlarge
CSS transition or animations (by :hover or any other mean)
In the de-facto standard, there is a event window.onresize to subscribe resize on a window/frame.
But there is no a standard event on the HTML content or DOM Elements.
I come across the following thought
DOM Level 3 event target only on window/document type
IE has onresize for Elements but it is IE only implementation
MutationObserver which replace Mutation Events, but it does not fit the need of "onresize"
naive JavaScript polling
MutationObserver is close(inner DOM changes), but it does not (yet) cross browser (IE10 does not support) and it generate noise, not CSS aware.
A naive JavaScript polling should work in all case, but it generate either delay or CPU waste of many poll.
As of July 2020, ResizeObserver is still un-official in W3C nor WhatWG but it is already supported by all major browsers since support Safari 13.1 since 2020-Mar-24.
FYI, there's a spec for a new ResizeObserver API. Chrome seems to be the only browser that has implemented it as of Aug 2018 (see caniuse.com), but there's at least one polyfill you can use now (which uses MutationObserver).
Yes there is not simple solution, that's not good.
I've found something very useful for this.: cross browser event based element resize
It's tricky, appending some needed html to the element that have to be listened and detects scrolling event.
Some html example from that page:
<div class="resize-triggers">
<div class="expand-trigger"><div></div></div>
<div class="contract-trigger"></div>
</div>
Also Some JS:
var myElement = document.getElementById('my_element'),
myResizeFn = function(){
/* do something on resize */
};
addResizeListener(myElement, myResizeFn);
removeResizeListener(myElement, myResizeFn);
But it works for elements those are able to have children, not for self-closing tags.
You can see the demo http://jsfiddle.net/3QcnQ/67/
Well, there is a easy library for that. Although there's nothing official how to listen on dimension changes of all types of elements and only window supports it at the moment we have luckily a polifill for that that works very accurate and supports all browsers even inclusive IE6+.
https://github.com/marcj/css-element-queries
You can find there a class ResizeSensor. To setup a listener on a element you can just do:
new ResizeSensor($('.myelements'), function() {
console.log('changed');
});
Given yourelement, when the size changes (ex. a text translation took place) you can doyourstuff(), including
ro.unobserve(yourelement);
var inilen = yourelement.offsetWidth;
var ro = new ResizeObserver( entries => {
for (let entry of entries) {
const cr = entry.contentRect;
if (inilen !== cr.width) {
doyourstuff();
}
}
});
ro.observe(<your element>);
In the future, we may have the luxury of the ResizeObserver everywhere, but for less recent browsers in 2021 we need to make do with a workaround. This article has already been posted, but it's pretty old and I think the solution might be overly complicated for modern browsers.
Still, the core idea remains: add an <object> element as a child with width: 100%; height: 100%;, and set a resize listener on its inner window object.
Here's some demo code that works in the latest Chrome and Firefox:
const div = document.getElementById('demo')
const obj = document.createElement('object')
obj.className = 'resize-detector'
obj.type = 'text/html'
obj.data = 'about:blank'
obj.addEventListener('load', function() {
// Initialize once.
handleResize()
// Add resize handler on the <object>'s inner window.'
obj.contentWindow.addEventListener('resize', function() {
handleResize()
})
})
div.appendChild(obj)
function handleResize() {
document.getElementById('size').innerHTML = `${div.offsetWidth}×${div.offsetHeight}`
}
.resizable {
/* Make this the offset parent of the <object> */
position: relative;
}
#demo {
width: 100px;
height: 100px;
background-color: #def;
/* Allow user resizing, for testing. */
resize: both;
overflow: hidden;
}
object.resize-detector {
display: block;
visibility: hidden;
left: 0;
top: 0;
width: 100%;
height: 100%;
}
<div id="demo" class="resizable">
<div id="size"></div>
</div>
It doesn't work inside the StackOverflow snippet because of some same-origin policy shenanigans, but here's a JSFiddle with the same code.
Here is nice piece of code that works fair in all browsers: http://www.imaputz.com/cssStuff/bigFourVersion.html
Since there are things that need to be computed for every particular table (extra space for scrollbar, fixed width for every cell), I'd like to create a javascript that modifies the table to make it scrollable.
Such scripts already exist, i.e. http://www.farinspace.com/jquery-scrollable-table-plugin/
but all of them need to specify the height of the table. I'd like to extract the height from CSS to make the script unobtrusive.
If I write the height as inline style, all browsers can read the table.style.height property. But when external style is used
<style>
.scrollTable { display: block; overflow: hidden; height: 200px; }
</style>
only Firefox can read the offsetHeight/clientHeight property as expected. Other browsers can't change table layout to block, I guess.
The only thing that I figured out is to read external CSS
function getCSS(rule,prop) {
if(!(document && document.styleSheets)) return;
var result="", ds = document.styleSheets;
for(var i=0; i<ds.length; i++) {
var r = getCSS1(ds[i],rule,prop);
if(r) result = r;
}
return result;
}
function getCSS1(sheet,rule,prop) {
var rules = sheet.cssRules? sheet.cssRules: sheet.rules;
var result = "";
for(var i=0; i<rules.length; i++) {
var r = rules[i].selectorText.toLowerCase();
if(r.indexOf(rule)==-1) continue;
if(r.lastIndexOf(rule)!=r.length-rule.length) continue;
var p = rules[i].style[prop];
if(p) result = p;
}
return result;
}
var height = getCSS(".scrollTable","height");
But the height might be specified otherwise: by id, another class or inherited, so I am probably in wrong way. Is there any other way how to guess the table height? I am about to give up.
In short
I want this code
<script src="scrollTable.js"></script> // load the script
<style> .scrollTable { height: 200px; } // set the CSS
<table class="scrollTable">...</table> // script should do all the tricks
My script can do all the tricks except for get the table height. How to extract it?
Why not enclose the <table> in a <div>? This way you should be able to the get the <div> height even if it's in an external CSS.
With jQuery you can use .css() function:
table_height = $("table.scrollTable").css("height");
This will return the height of your table including px. So you can extract only the number with some other javascript function.
Assuming you are using jQuery (you did mention a jQuery plugin), you can calculate the height of an element using $('table.selector').height().
Is getComputedStyle the answer ? See here : http://blog.stchur.com/2006/06/21/css-computed-style/
You could use
document.getElementById("ID").clientHeight
It worked for me in IE8 and Chrome when I tested height set via
html
inline style
style sheet
Here's what I did:
<table height="350" id="one">
<table>
<table style="height:400px;" id="two">
<table>
<table class="height" id="three">
<table>
and
alert(document.getElementById("one").clientHeight);
alert(document.getElementById("two").clientHeight);
alert(document.getElementById("three").clientHeight);
Working example: http://jsfiddle.net/jasongennaro/h9B2j/
EDIT
Something else must be interfering with the code, I think, as
document.getElementById("").clientHeight;
works for me even without the height being set.
See another example here: http://jsfiddle.net/jasongennaro/h9B2j/2/
I get this problem in IE7 when running a piece of code that uses jquery and 2 jquery plugins. The code works in FF3 and Chrome.
The full error is:
Line: 33
Char: 6
Error: bg is null or not an object
Code: 0
URL: http://localhost/index2.html
However line 33 is a blank line.
I am using 2 plugins: draggable and zoom. No matter what I do to the code it is always line 33 that is at fault. I check the source has update via view source but I feel this could be lying to me.
<body>
<div id="zoom" class="zoom"></div>
<div id="draggable" class="main_internal"><img src="tiles/mapSpain-smaller.jpg" alt=""></div>
<script type="text/javascript">
$(document).ready(function() {
$('#draggable').drag();
$('#zoom').zoom({target_div:"draggable", zoom_images:new Array('tiles/mapSpain-smaller.jpg', 'tiles/mapSpain.jpg') });
});
</script>
</body>
Essentially what I am trying to do is recreate the Pragmatic Ajax map demo with jQuery.
It would appear that the second line of this snippet is causing the trouble:
bg = $(this).css('background-position');
if(bg.indexOf('%')>1){
It seems to be trying to select the background-position property of #draggable and not finding it? Manually adding a background-position: 0 0; didn't fix it. Any ideas on how to get around this problem?
I tried using the MS Script Debugger but that is nearly useless. Can't inspect variables or anything else.
A bit more digging about on the Interweb has revealed the answer: IE doesn't understand the selector background-position. It understands the non-standard background-position-x and background-position-y.
Currently hacking something together to workaround it.
Nice one, Redmond.
To get around the fact that Internet Explorer does not support the "background-position" CSS attribute, as of jQuery 1.4.3+ you can use the .cssHooks object to normalize this attribute between browsers.
To save yourself some time, there is a background position jQuery plugin available that allows both "background-position" and "background-position-x/y" to work as expected in browsers that don't support one or the other by default.
It is interesting. IE8 doesn't understand getter backgroundPosition, but it understands setter.
$('.promo3').mousewheel(function(e,d){
var promo3 = $(this);
var p = promo3.css('backgroundPosition');
if (p === undefined) {
p = promo3.css('backgroundPositionX') + ' ' + promo3.css('backgroundPositionY');
}
var a = p.split(' ');
var y = parseInt(a[1]);
if (d > 0) {
if (y < -1107) y += 1107;
y -= 40;
}
else {
if (y > 1107) y -= 1107;
y += 40;
}
promo3.css('backgroundPosition', a[0] + ' ' + y + 'px');
return false;
});
It works great in IE8 and IE8 compatible view.
This worked for me:
if (navigator.appName=='Microsoft Internet Explorer')
{
bg = $(drag_div).css('backgroundPositionX') + " " + $(drag_div).css('backgroundPositionY');
}
else
{
bg = $(drag_div).css('background-position');
}
hope it does for you.
You may want to check to make sure that you are loading your js files in the correct order so that any dependencies are taken into account.
A bit of thinking (and a cup of tea) later I came up with:
if(bg == 'undefined' || bg == null){
bg = $(this).css('background-position-x') + " " + $(this).css('background-position-y');
}
Unfortunately it returns center center despite the online resources I can find state it should return 0 0 if the values are undefined.
Beginning to wonder if there is an actual fix/workaround to this. A lot of people have tried and all so far fail to catch all edge cases.
The camelCase version of backgroundPosition seems viable but I don't know enough of jQuery to make an accurate assessment of how to go about it - from what I have read you can only use camelCase as getters if the property has been set previously. Please tell me if I am mistaken.
However line 33 is a blank line.
It'll be line 33 of one of your .js files, not line 33 of the HTML itself. IE fails to report which actual file the error was in. Look at line 33 of each .js for something about ‘bg’; if the worst comes to the worst you can start inserting newlines at the start of each .js and see whether the line number changes.
I check the source has update via view source but I feel this could be lying to me.
View source will always show you what IE got from the server. It won't show any updates to the DOM.
try backgroundPosition istead
Also, make sure that 'this' exists and that your request for an attribute returns a value. IE will throw this kind of errors when you try to call a method on a property that does not exist, therefore bg is null or null an object. if you dont care about IE you can do bg = $(this)... || '' so that theres always something referenced.
Also, unrelated to the error you're getting, but is your index value of 1 correct? Did you mean -1 ?
Yupp, Try background-position instead or just set the background-position with jquery before you call it. Ill guess one often knows the positions through CSS before calling it. It isnt pretty, but somehow it did the trick for me.)
eg:
//set it in with javascript.
$("someid").css("background-position", "10px 0");
...
//do some funky stuff
//call it
$("someid").css("background-position");
//and it would return "10px 0" even in IE7
if nothing helps, it's also possible to make the following trick.
We can replace a background of an element by an inner absolutely positioned element (with the same background). The coordinates will be replaced by left and top properties. This will work in all browsers.
For better understanding, please, check the code:
Before
<div></div>
div {
background: url(mySprite.png);
background-position: -100px 0;
}
After
<div>
<span></span>
</div>
div {
position: relative;
overflow: hidden;
width: 100px; /* required width to show a part of your sprite */
height: 100px; /* required height ... */
}
div span {
position: absolute;
left: -100px; /* bg left position */
top: 0; /* bg top position */
display: block;
width: 500px; /* full sprite width */
height: 500px; /* full sprite height */
background: url(mySprite.png);
}
This solution is not very flexible, but it helped me to show icons hover state properly.
You can't use dashes in the jquery css function. You have to do it in camelCase:
.css('backgroundPosition') or .css('backgroundPositionX') and .css('backgroundPositionY') for IE