Detect whether an element has position:fixed (possibly by parent element) via jQuery
Asked Answered
M

6

12

Update I'd like to avoid walking back through all the element's parents, testing each in turn, but that is the best solution I've managed to find so far, as in this answer.


Inside an event handler, I would like to detect whether the target element's position is relative to the viewport (fixed) or the document (static, relative or absolute).

Given that an element's position might be fixed because it has "position:fixed", or because one of its parents is "position:fixed", what is an efficient way of detecting fixed positioning for the element?

For example:

CSS

#one {position:relative; height:10px; width:10px}

#two-wrapper {position:fixed; bottom:0; height:10px; width:10px}
#two {height:10px; width:10px}

HTML

<div id="one" class="trigger-event">element one</div>

<div id="two-wrapper">
    <div id="two" class="trigger-event">element two</div>
</div>

JS

$(document).ready(function(){
    $('.trigger-event').on('mouseenter', function(event){
        var isFixed = .... ????

        if (isFixed) {
            $(event.target).css('background','red'); 
        } else {
            $(event.target).css('background','green'); 
        }
    });
});
Mylesmylitta answered 20/10, 2013 at 5:43 Comment(0)
M
14

Here is a solution based on walking through the element's parents, checking the CSS position value for each in turn.

Is this the most efficient way, or is there a way to detect the effective positioning by only examining the element itself?

http://jsfiddle.net/Q4mSJ/

CSS

.trigger-event {background:#ccc}

#one {position:relative; height:50px; width:50px}

#two-wrapper {position:fixed; bottom:0; height:50px; width:50px}
#two {height:50px; width:50px}

HTML

<div id="one" class="trigger-event">element one</div>

<div id="two-wrapper">
    <div id="two" class="trigger-event">element two</div>
</div>

JS

function elementOrParentIsFixed(element) {
    var $element = $(element);
    var $checkElements = $element.add($element.parents());
    var isFixed = false;
    $checkElements.each(function(){
        if ($(this).css("position") === "fixed") {
            isFixed = true;
            return false;
        }
    });
    return isFixed;
}

$(document).ready(function(){
    $('.trigger-event').on('mouseenter', function(event){
        var isFixed = elementOrParentIsFixed(event.target);

        if (isFixed) {
            $(event.target).css('background','red'); 
        } else {
            $(event.target).css('background','green'); 
        }
    });
});
Mylesmylitta answered 20/10, 2013 at 7:28 Comment(2)
very late congrats, but +1 :-) because I saw that your function elementOrParentIsFixed() is almost the same like mine below, only that mine is a jquery plugin instead of a function.Inoffensive
But this code also needs jQuery loaded to work by copy/paste without really needing a framework for this easy vanilla written task... So, surely not the best answer...Speedometer
M
9

Using $(selector).offsetParent() you can search through the ancestors of the selector without having to load all the elements parents. It will still have to walk through the elements parents but will stop once it finds the closest element with the chosen position (relative, absolute, or fixed).

http://jsfiddle.net/89y1buz9/11/

CSS:

div {
border: 1px solid #ccc;
}
#outer {
    height: 200px;
    width: 120px;
    position: fixed;
    padding-top: 20px;
}
#inner {
        width: 95px;
    height: 150px;
    margin: 0px auto;
    position: relative;
}
#clickme {
    width: 70px;
    height: 50px;
    cursor: pointer;
    text-align: center;
    line-height: 40px;
    margin: 20px auto;
}

HTML:

<div id="outer">
    <div id="inner">
        <div id="clickme">ClickMe</div>
    </div>
</div>

Javascript:

function isFixed(ele, posType) {
    var eleCheck = ele.offsetParent(),
        returnVal;
    posType = posType || "fixed";
    if (eleCheck.prop("tagName") === 'HTML') {
        return false;
    }
    returnVal = (eleCheck.css("position") !== posType) ? isFixed(eleCheck): posType;
    return returnVal;
}

Usage: Call it with or without the second argument it defaults to search for a fixed element

$(function () {
    $("#clickme").on("click", function () {
        if (isFixed($(this), "fixed") === "fixed") {
            $(this).css("background", "red");
        } else {
            $(this).css("background", "green");
        }
    });
});
Maltase answered 10/10, 2014 at 18:5 Comment(0)
D
5

Here is what I am now using to detect elements that may be nested inside a position:fixed parent. (Based on another answer which had issues with null values.)

// Returns true if `node` uses css position:fixed or has a parent that does so.
function isFixedPosition(node) {
    while (node && node.nodeName.toLowerCase() !== 'body') {
        if (window.getComputedStyle(node).getPropertyValue('position').toLowerCase() === 'fixed')
            { return true; }
        node = node.parentNode;
    }
    return false; // if got this far
}
Drinker answered 11/1, 2020 at 4:15 Comment(1)
THAT's a nice vanilla way, great idea! May I suggest to change toLowerCase to .match(/fixed|sticky/i)Speedometer
I
4

UPDATED, see below...

I dropped by here while searching a pure javascript solution for this... But I hope that somebody will like my jquery version.

Here are two functions, which work slightly different, but the performance differences are minimal. I did not measure it, but according this test on jsperf "each vs filter" the .each()version is slightly faster.

Nevertheless, I like the .filter() version because it's so short and clear.

$.fn.isfixed = function(){
    var el = $(this);
    var all = el.add(el.parents());
    return all.filter(function(){
        return $(this).css("position") === "fixed";
    }).length > 0;
};

$.fn.isfixed = function(){
    var el = $(this);
    var all = el.add(el.parents());
    var ret = false;
    all.each(function(){
        if ($(this).css("position") === "fixed") {
            ret = true;
            return false;
        }
    });
    return ret;
};

Usage is $('path > to > your > element').isfixed() and returns true or false

UPDATE

And here's the pure javascript version. It turned out, that to detect el.style.position (fixed/absolute/relative) does not work, don't know why, but window.getComputedStyle(el)... does.

var isfixed = function(elm) {
    var el;
    if (typeof elm === 'object') el = elm[0] || elm;
    else if (typeof elm === 'string') el = document.querySelector(elm);
    while (typeof el === 'object' && el.nodeName.toLowerCase() !== 'body') {
        if (window.getComputedStyle(el).getPropertyValue('position').toLowerCase() === 'fixed') return true;
        el = el.parentElement;
    }
    return false;
};

It accepts three kind of element parameters:
as javascript object:
var obj = document.querySelector('div#myID > span');
or
var obj = document.getElementById('span#someID');

as jquery object:
var obj = $('div#myID > span');

or as selector only:
var obj = 'div#myID > span';

Usage is simply isfixed(obj); and delivers boolean true or false

And here's finally the mixed solution (pure javascript as jquery plugin)

$.fn.isfixed = function(){
    var el = this[0];
    while (typeof el === 'object' && el.nodeName.toLowerCase() !== 'body') {
        if (window.getComputedStyle(el).getPropertyValue('position').toLowerCase() === 'fixed') return true;
        el = el.parentElement;
    }
    return false;
};

Usage: $('div#myID > span').isfixed() as in examples above.
Hope you'll like it.

Inoffensive answered 7/5, 2017 at 17:36 Comment(0)
A
1

I took some of the above examples and made simple js vanilla code to get position of element, so you can use it in jQuery and also without jQuery.

function getStylePosition(element){
  return window.getComputedStyle(element).getPropertyValue('position');
}

// use it like this
var element = document.getElementsByTagName('div')[0];
// js only
alert(getStylePosition(element));

// jQuery
alert(getStylePosition($('div')[0]));
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div style="position:fixed;"></div>
Anthropogenesis answered 28/6, 2018 at 9:33 Comment(1)
This doesn't work in the case where an element has position:absolute inside a parent that has position:fixed.Drinker
R
0
$(document).ready(function(){
    $('.trigger-element').on('mouseenter', function(event){
        var position= $(this).css("position");

        if (position=="fixed") {
            $(event.target).css('background','red'); 
        } else {
            $(event.target).css('background','green'); 
        }
    });
});

jsFiddle

Remission answered 20/10, 2013 at 5:47 Comment(4)
Sorry, this only seems to work when "position:fixed" is set directly on the target element. It doesn't work when an element is static but its parent is fixed.Mylesmylitta
thanks, I guess that's one piece of the "walk back through the element's parents" approach. .parents() can take a selector argument. Is there a selector I can use to filter only parents with position:fixed?Mylesmylitta
You could add a class on the parent with position:absolute and select that with .parents(). Can't use selectors for individual CSS attributes though.Irmairme
Code is great, but an answer should contain why it the code works.Poppycock

© 2022 - 2024 — McMap. All rights reserved.