Dynamically detect if position:sticky is supported by the browser
Asked Answered
L

2

7

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.

Laszlo answered 13/2, 2020 at 18:26 Comment(0)
A
2

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());
Aceves answered 13/2, 2020 at 18:27 Comment(5)
The solution is an interesting one! The principle is good, but it is not usable as it is... It is obviously a piece of Modernizr. To have it as a standalone JS function would be ideal.Laszlo
Just extract the test case from the .addTest() call o.OAceves
I tried. It's not clear where the prefixes object comes from.Laszlo
github.com/Modernizr/Modernizr/blob/master/src/prefixes.jsAceves
I just tested the "browserSupportsPositionSticky" on a version of iPad Safari where the inspector doesn't apply position: sticky (prefixed or not) and in dev tools calls it an invalid value for position and this function returned that it was supported. This was the only non-sticky-supporting browser I tested it on, so I doubt it's the only one that fails to detect lack of sticky support.Entertainer
P
14

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 ... */
}
Peti answered 13/2, 2020 at 18:36 Comment(4)
What if CSS and CSS.supports are not supported, but position:sticky is?Laszlo
<html> <head> </head> <body> </body> <script> alert("ok"); if(CSS.supports("position:sticky")) { document.body.style.backgroundColor = "red"; } </script> </html>Tallinn
@Laszlo Great question re: support! There seems to currently be ~95% support for CSS.supports and ~92% for position:sticky, according to caniuse.com. With the exception of Safari (incl iOS), introduction of sticky trails that of supports in all browsers, so I wouldn't generally hesitate to use this technique.Peti
I've found that this works properly in cases where the accepted answer falsely reports support. I'd be in favor of this being the accepted answer.Entertainer
A
2

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());
Aceves answered 13/2, 2020 at 18:27 Comment(5)
The solution is an interesting one! The principle is good, but it is not usable as it is... It is obviously a piece of Modernizr. To have it as a standalone JS function would be ideal.Laszlo
Just extract the test case from the .addTest() call o.OAceves
I tried. It's not clear where the prefixes object comes from.Laszlo
github.com/Modernizr/Modernizr/blob/master/src/prefixes.jsAceves
I just tested the "browserSupportsPositionSticky" on a version of iPad Safari where the inspector doesn't apply position: sticky (prefixed or not) and in dev tools calls it an invalid value for position and this function returned that it was supported. This was the only non-sticky-supporting browser I tested it on, so I doubt it's the only one that fails to detect lack of sticky support.Entertainer

© 2022 - 2024 — McMap. All rights reserved.