Dynamically detect if position:sticky is supported by the browser - javascript

Is there a way to detect, using Javascript or jQuery, whether the browser supports position:sticky or not?
I know most modern browsers support it, but some older browsers and some mobile browsers don't.
I'm not interested in a polyfill. I want to take certain actions only if position:sticky works, otherwise just leave things as they are.

A great and powerful way to check if a CSS feature is available is to use the CSS.supports JavaScript function:
if (CSS && CSS.supports && CSS.supports("position", "sticky")) {
// awesome: position:sticky is supported on this browser!
} else {
// fallback: I cannot rely on position:sticky!
}
I hope this answers your question, but I think it's worth mentioning that if you're tempted to use CSS.supports() you should at least consider responding to a lack of feature just using CSS alone. While JavaScript is a great way to make dynamic changes to a page, you often don't need it to have your page respond to a lack of a feature. This is especially for CSS features like sticky.
E.g.
/*
...
Basic styles for old browsers that don't support sticky go here.
...
*/
#supports (position:sticky) {
/* Overrides to the above styles for modern "sticky" browsers go here */
}
And even then, you often don't even need to go this fancy. For example, let's say you have a nav bar that you would like to be position:sticky if possible, but otherwise just position:absolute. Even though some browsers don't understand sticky, you can say:
.my-nav-bar {
/* The fallback goes first */
position: absolute;
/* This 'enhancement' is ignored if not understood, */
/* but overrides the previous value if the browser supports it. */
position: sticky;
top: 50px;
/* ... etc ... */
}

From modernizr:
/*!
{
"name": "CSS position: sticky",
"property": "csspositionsticky",
"tags": ["css"],
"builderAliases": ["css_positionsticky"],
"notes": [{
"name": "Chrome bug report",
"href":"https://bugs.chromium.org/p/chromium/issues/detail?id=322972"
}],
"warnings": ["using position:sticky on anything but top aligned elements is buggy in Chrome < 37 and iOS <=7+"]
}
!*/
define(['Modernizr', 'createElement', 'prefixes'], function(Modernizr, createElement, prefixes) {
// Sticky positioning - constrains an element to be positioned inside the
// intersection of its container box, and the viewport.
Modernizr.addTest('csspositionsticky', function() {
var prop = 'position:';
var value = 'sticky';
var el = createElement('a');
var mStyle = el.style;
mStyle.cssText = prop + prefixes.join(value + ';' + prop).slice(0, -prop.length);
return mStyle.position.indexOf(value) !== -1;
});
});
Source: https://github.com/Modernizr/Modernizr/blob/master/feature-detects/css/positionsticky.js
with:
prefixes = ' -webkit- -moz- -o- -ms- '.split(' ');
Source: https://github.com/Modernizr/Modernizr/blob/master/src/prefixes.js
Working example (w/o Modernizr dependencies):
function browserSupportsPositionSticky() {
var prop = 'position:';
var value = 'sticky';
var prefixes = ' -webkit- -moz- -o- -ms- '.split(' ');
var el = document.createElement('a');
var mStyle = el.style;
mStyle.cssText = prop + prefixes.join(value + ';' + prop).slice(0, - prop.length);
return mStyle.position.indexOf(value) !== -1;
};
console.log(browserSupportsPositionSticky());

Related

What is a proper way to detect the support of css "flex-box" and "flex-wrap"?

I'm looking for a solution how we can detect the support of css flex-box and flex-wrap by JavaScript.
I'm aware of modernizr which can do the job but my client does not allow us to load any scripts inside the head section, unfortunately this does not work when loading in the footer.
What is a proper way to achieve this detection on all kind of browsers / devices?
how we can detect the support of css flex-box and flex-wrap by
JavaScript.
Create an element and check the style property. If it is supported, it will return nothing i.e. '' else it will return undefined.
For example, if you run the below snippet in Chrome you will get undefined for columns and '' for flex-wrap.
Snippet:
console.log('flex = ' + document.createElement("p").style.flex);
console.log('columns = ' + document.createElement("p").style.columns);
console.log('flex-wrap = ' + document.createElement("p").style.flexWrap);
Although you have mentioned only Javascript in your question, but there is a CSS way of feature detection as well.
The #supports rule, also called CSS Feature Queries.
You provide the CSS declaration as the condition and the browser will execute that to return whether it supports or not. For example, the following CSS will apply green background color if flex is supported.
#supports (display: flex) {
div { background-color: #0f0; }
}
The browser support is good amongst all modern browsers, barring IE (as usual). For IE and (Safari < 9), you will need to keep a fallback option when #supports rule fails.
Combining the above two, there is an API around that as well which you can use in Javascript to do feature detection.
var isColumnSupported = CSS.supports('columns', '');
console.log('columns supported: ' + isColumnSupported);
var isFlexWrapSupported = CSS.supports('flex-wrap', 'wrap');
console.log('flex-wrap supported: ' + isFlexWrapSupported);
Since CSS.supports() is not supported on IE
This robust method can test any property:value support:
var cssPropertySupported = (function(){
var mem = {}; // save test results for efficient future calls with same parameters
return function cssPropertySupported(prop, values) {
if( mem[prop+values] )
return mem[prop+values];
var element = document.createElement('p'),
index = values.length,
name,
result = false;
try {
while (index--) {
name = values[index];
element.style.display = name;
if (element.style.display === name){
result = true;
break;
}
}
}
catch (pError) {}
mem[prop+values] = result;
return result;
}
})();
///////// TEST: ////////////////////
console.log(
cssPropertySupported('display', ['-ms-flexbox', '-webkit-box', 'flex'])
)
You manually need to provide the test function all the possible property names since the code cannot guess (too many possibilities). This keeps the test code slim instead of it have all possible property names already within it.

Check for "-ms-expand" support

i'm trying to make a little script to style "select" tags in my html testing for the support of the "appearance" style property:
if ("webkitAppearance" in select.style ||
"MozAppearance" in select.style ||
"oAppearance" in select.style ||
"appearance" in select.style ||
"-ms-expand" in select.style) {
return;
}
// else apply wrapper and style it.
The problem is that i don't know how to check for -ms-expand property, because it's not working and i don't want to user browser version sniffing in this case.
You can't check -ms-expand in javascript, because it is pseudo-element, and it do not affect to content. You can't detect it like ::before/::after in Modernizr, but -ms-expand is enabled in IE 10+, so better will be to detect is IE 10 or higher by javascript:
Detection IE 11:
!window.ActiveXObject && "ActiveXObject" in window
Detection IE 10:
var isIE10 = false;
/*#cc_on
if (/^10/.test(#_jscript_version)) {
isIE10 = true;
}
#*/
Not the most optimal solution in terms of performance, but you could try this:
var expandSupport = (function(){
try{
var style = document.createElement("style");
style.textContent = "#ie10-test::-ms-expand{ }";
document.body.appendChild(style);
var supported = !!style.sheet.rules.length;
document.body.removeChild(style);
return supported;
} catch(e){ return false; }
}());
document.body.appendChild(document.createTextNode(expandSupport ? "Your browser appears to support the -ms-expand pseudo-element" : "Your browser doesn't appear to support the -ms-expand pseudo-element"));
Fiddle here.
The reason this works is because browsers will discard any selectors they don't support or can't interpret, which means any browser that doesn't understand what the "::-ms-expand" pseudo-element is probably isn't IE10 or up.
Essentially, all that this code is doing is creating a dummy <style> tag, adding a ruleset that only IE10+ is expected to support, and reporting back the number of rules found in it.

How to generically test if mediaqueries are supported?

May sound odd, but I'm looking for a simple way to test if any mediaqueries are supported on a browser.
I know there is respondjs, which comes with a window.matchMedia polyfill, but even using it, I still have to query specific queries like:
window.matchMedia("all and (min-width: 400px"));
which returns an obj.matches = true/false.
What I'm looking for is a generic way to test, "if mediaQueries are supported", yes or no.
I'm currently using:
window.matchMedia("screen and (orienation:landscape),
screen and (orientation:portrait)");
but I don't really like this way of testing.
I also tried:
window.matchMedia("all");
but this (of course) returns true in IE8 when using the matchMedia polyfill. Also, I can't test for matchMedia itself, because I would miss out on a lot of browsers that support media queries, but not window.matchMedia (caniuse).
Question:
Is there a nice and simple way to test for media query support? If possible, I would not want to use window.matchMedia at all to do that.
EDIT:
I also checked Modernizr, which also tests for a specific mediaQuery condition (mq):
testMediaQuery = function( mq ) {
var matchMedia = window.matchMedia || window.msMatchMedia;
if ( matchMedia ) {
return matchMedia(mq).matches;
}
var bool;
injectElementWithStyles('#media ' + mq + ' { #' + mod + ' { position: absolute; } }', function( node ) {
bool = (window.getComputedStyle ?
getComputedStyle(node, null) :
node.currentStyle)['position'] == 'absolute';
});
return bool;
},
So no generic test, if mediaQueries are supported.
U can choose this option in modernizr
http://modernizr.com/download/

Are there any javascript code (polyfill) available that enable Flexbox (2012, css3), like modernizr?

I'm looking for any javascript library that like modernizr (which actually does not) enables flexbox for "older browsers" (a polyfill).
Yea I know this is a really new feature (infact "there aren't" is a valid answer), but I'm hoping for something like this, I always have hard time with horizontal + vertical centering, this will really help and shorten the work.
I mean this flexbox: http://weblog.bocoup.com/dive-into-flexbox/ (the newest)
It might be too early for this. WebKit implemented it fairly recently, there's no hint of support in any mobile WebKit at all, Opera just released support for it, and Gecko's implementation is still in alpha. IE? Hah.
But as far as I can tell, no, there's no polyfill for the new flexbox. Flexie supports the old flexbox, and has a ticket open to support the new syntax... maybe you could give them a hand?
You could always use the old flexbox, I suppose, but then you're obsolete out of the gate. Sucky situation.
You're going to have to create your own.
http://www.sitepoint.com/detect-css3-property-browser-support/ has a section titled "Rolling Your Own Detection Code"
Basically you'll need something like this:
// detect CSS display:flex support in JavaScript
var detector = document.createElement("detect");
detector.style.display = "flex";
if (detector.style.display === "flex") {
console.log("Flex is supported");
}
else
{
console.log("Flex is not supported");
}
To expand on that and create a function:
function DetectDisplayValue(val)
{
// detect CSS display:val support in JavaScript
//
var detector = document.createElement("detect");
detector.style.display = val;
if (detector.style.display === val) {
console.log("Display value: " + val + " is supported");
}
else
{
console.log("Display value: " + val + " is not supported");
}
}

Using mootools Fx to modify the zoom of a div

I have a case where I'd like to animate the zoom style of a div (and it's entire contents). Legibility is not an issue, as this will be to 'pop' the element into view. Now I can get just about every other style aspect animating with Fx, but I can't seem to get this working for the zoom.
This is in chrome (though obviously I want to get it browser agnostic as well).
Using the Elements tab in the Chrome dev tools I can manually set the zoom style on the html element and it behaves accordingly, so I knew it must be possible to modify in code. Using the answer on this question: zoom css/javascript
I can get the element to zoom in and out with code:
dialog.setStyle('WebkitTransform', 'scale(0.1)')
Now I could write a 'native' function to do this, but then I'd lose out on all the great animation options in Fx. Can anyone suggest an elegent way to achieve this with Fx?
yes, you need to hack some of the CSS parsers in the mootools core to enable FX to work with them.
check this fun example I did a while back for another SO problem: http://jsfiddle.net/dimitar/ZwMUH/ - click on any 2 icons to swap them and it will transition them via scale.
or this light box base class I wrote that also uses it: http://jsfiddle.net/dimitar/6creP/
at its basic, start by modding the parsers:
Element.Styles.MozTransform = "rotate(#deg) scale(#)";
Element.Styles.MsTransform = "rotate(#deg) scale(#)";
Element.Styles.OTransform = "rotate(#deg) scale(#)";
Element.Styles.WebkitTransform = "rotate(#deg) scale(#)";
Object.append(Fx.CSS.Parsers, {
TransformScale: {
parse: function(value) {
return ((value = value.match(/^scale\((([0-9]*\.)?[0-9]+)\)$/i))) ? parseFloat(value[1]) : false;
},
compute: function(from, to, delta) {
return Fx.compute(from, to, delta);
},
serve: function(value) {
return 'scale(' + value + ')';
}
}
});
also relevant, define public and scripting vers of all styles cross browser:
transforms: {
computed: ['transformProperty', 'WebkitTransform', 'MozTransform', 'OTransform', 'msTransform'],
raw: ['transform', '-webkit-transform', '-moz-transform', '-o-transform', 'msTransform']
};
detection method which will loop through the transforms defined above and return the first one that the element supports as the definitive property to work with in the future, or opacity as fallback if unavailable:
var testEl = new Element("div"),
self = this;
this.scaleTransform = this.options.transforms.computed.some(function(el, index) {
var test = el in testEl.style;
if (test) {
self.prop = self.options.transforms.raw[index];
}
return test;
});
if (!this.prop) {
this.prop = "opacity";
}
then this.prop will refer to the correct browser property, vendor prefixed or opacity as fallback for tweening/morphing whereas this.scaleTransform will be a boolean that points to the ability to scale - you can then check against that to see if its supported when you are creating the morph object.
The object itself would be like this:
var morphObj = {};
morphObj[this.prop] = ["scale(0)", "scale(1)"]; // call it dynamically
el.morph(morphObj);
other solutions are available such as this plugin http://mootools.net/forge/p/fx_tween_css3, there's also one by Theiry Bela I know of: https://github.com/tbela99/Fx.css
its also going to be natively available in mootools 2.0
have fun.

Categories