Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make jquery :hidden and :visible selectors work #1048

Open
thomaslule opened this issue Feb 28, 2015 · 13 comments
Open

Make jquery :hidden and :visible selectors work #1048

thomaslule opened this issue Feb 28, 2015 · 13 comments
Labels
feature layout Blocked on implementing a layout engine

Comments

@thomaslule
Copy link

Hello,

I'm probably missing something here but I can't make jQuery :hidden selector work.

Shouldn't this print true ?

var jsdom = require("jsdom");

jsdom.env({
    html: '<div style="display: none;" />',
    scripts: [ "http://code.jquery.com/jquery-2.1.3.min.js" ],
    done: function (errors, window) {
        if (errors) throw errors;
        console.log(window.$('div').is(':hidden')); // prints false
    }
});

(jsdom version is 3.1.2)

@Sebmaster
Copy link
Member

Works with jQuery v1. Looks like we might run into some compat issues which were removed with v2.

@Sebmaster Sebmaster added the bug label Feb 28, 2015
@Sebmaster
Copy link
Member

There we go:
jQuery 2.0 uses offsetHeight and offsetWidth to determine visibility (whereas it used multple factors in 1.0), however we don't have layouting in jsdom. This will probably not work for a looooong time (if ever); use jQuery 1.0 instead.

@thomaslule
Copy link
Author

Thank you, it's better but I just bumped into another problem: if the tested element isn't hidden itself but is inside a hidden one, it doesn't work.

Updated code:

var jsdom = require("jsdom");

jsdom.env({
    html: '<div style="display: none;"><span /></div>',
    scripts: [ "http://code.jquery.com/jquery-1.11.2.min.js" ],
    done: function (errors, window) {
        if (errors) throw errors;
        console.log(window.$('span').is(':hidden')); // prints false
    }
});

@Sebmaster
Copy link
Member

Yes, we won't be able so solve that without proper layouting (to properly calculate offsetWidth/offsetHeight). There might be a hack possible where you run through all parent elements and check if they have display: none set, but I doubt well add something like this in jsdom.

@thomaslule
Copy link
Author

I'll do that check with some other way, thanks for your inputs :)

@thomaslule
Copy link
Author

for anyone interested, this workaround looks okay with jQuery v1:

jQueryElt.is(':hidden') || jQueryElt.parents().is(':hidden');

wmfgerrit pushed a commit to wikimedia/mediawiki-extensions-Popups that referenced this issue Feb 23, 2017
Given this is a jsdom environment assertions using jQuery's :visible
have been changed to check the display property for visibility.

See jsdom/jsdom#1048

Change-Id: Ifad8067c0b50053a94ac977ee1f1f5a3066bfa16
@domenic
Copy link
Member

domenic commented Oct 12, 2017

Deleting +1 comments as they are not helpful; if they persist we will lock this issue.

@jsdom jsdom deleted a comment from James-E-Adams Oct 12, 2017
@jsdom jsdom deleted a comment from spin Oct 12, 2017
@jsdom jsdom deleted a comment from b01 Oct 12, 2017
@domenic domenic added selectors CSS Selectors support and removed bug labels Apr 23, 2018
@viddo
Copy link
Contributor

viddo commented Apr 25, 2018

For what it's worth, doing something similar to what's suggested in #135 (comment) works, e.g.:

Object.defineProperty(window.HTMLElement.prototype, 'offsetHeight', {
  configurable: true,
  get: () => 123, // i.e. return a fixed height for all HTML elements
});

Depending on your use-case it might more or less sense of course.

@Zirro Zirro added layout Blocked on implementing a layout engine feature and removed selectors CSS Selectors support labels May 27, 2018
@jcubic
Copy link

jcubic commented Jul 1, 2018

In jQuery 3 the visible pseudo selector look like this:

jQuery.expr.pseudos.visible = function( elem ) {
     return !!( elem.offsetWidth || elem.offsetHeight || elem.getClientRects().length );
};

so I've used this code:

window.Element.prototype.getClientRects = function() {
    var node = this;
    while(node) {
        if(node === document) {
            break;
        }
        // don't know why but style is sometimes undefined
        if (!node.style || node.style.display === 'none' || node.style.visibility === 'hidden') {
            return [];
        }
        node = node.parentNode;
    }
     var self = $(this);
    return [{width: self.width(), height: self.height()}];
};

@davidtorroija
Copy link

davidtorroija commented Mar 6, 2020

In jQuery 3 the visible pseudo selector look like this:

jQuery.expr.pseudos.visible = function( elem ) {
     return !!( elem.offsetWidth || elem.offsetHeight || elem.getClientRects().length );
};

so I've used this code:

window.Element.prototype.getClientRects = function() {
    var node = this;
    while(node) {
        if(node === document) {
            break;
        }
        // don't know why but style is sometimes undefined
        if (!node.style || node.style.display === 'none' || node.style.visibility === 'hidden') {
            return [];
        }
        node = node.parentNode;
    }
     var self = $(this);
    return [{width: self.width(), height: self.height()}];
};

I received an error of maximum stack I guessed right and modified something:

I changed this part return [{width: self.width(), height: self.height()}]; to return [{width:1111, height: 1111}]; and it works, because I don't care the width and height I'm only needing to use the :visible pseudo selector.

@OmbraDiFenice
Copy link

expanding a little bit on the previous workaround I ended up with this, which seems to work also in the case of nested elements

window.Element.prototype.getClientRects = function() {
    let node = this;
    let visible = false;
    while(node) {
        if(node === document) {
            break;
        }
        // don't know why but style is sometimes undefined
        if (!node.style || node.style.display === 'none' || node.style.visibility === 'hidden') {
            return [];
        }
        visible = true;
        node = node.parentNode;
    }
    return visible ? [{width: 111, height: 111}] : [];
};

the aim is of course just to unblock :visible (and :hidden) jQuery selectors

@jcubic
Copy link

jcubic commented Feb 27, 2022

@OmbraDiFenice this function is not recursive, that visible variable is kind of useless. You will never end with the second branch of the conditional operator. Unless you call it on $(document).is(':visible'), which is kind of stupid.

@OmbraDiFenice
Copy link

you're right @jcubic, my attempt wasn't to stop the recursion but rather to try to return the empty array in cases where a parent element wasn't visible. But regardless, my visible variable is totally useless XD For some reason it was fixing my tests but I tested a bit better (with a fresh mind, it's a new day over here 😃) and of course it does nothing. I don't know why my code started working after doing that, but clearly it wasn't because of that change.

Sorry for the confusion, in my defense I put together this "visible" stuff in like 5 minutes but I should know better by now and avoid such rookie mistakes 😅

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
feature layout Blocked on implementing a layout engine
Projects
None yet
Development

No branches or pull requests

8 participants