Heinzi
Heinzi

Reputation: 172280

Ignore visibility of outer elements and select the first visible child element in a jQuery selector

HTML:

<div class="outer">
  <div id="inner1" class="inner" style="display: none"></div>
  <div id="inner2" class="inner"></div>
  <div id="inner3" class="inner"></div>
</div>

JavaScript (fiddle):

var $first_visible = $("div.inner:visible:first");

This returns the first visible inner div, which is inner2.

However, as soon as the outer div is hidden (let's say I want to fade it in at some later time):

<div class="outer" style="display: none">
  <div id="inner1" class="inner" style="display: none"></div>
  <div id="inner2" class="inner"></div>
  <div id="inner3" class="inner"></div>
</div>

all inner divs are considered hidden and the selector does not return inner2 any more.

How would I need to modify my jQuery selector to ignore the container's visibility?

Upvotes: 13

Views: 1728

Answers (8)

Tiago Valente
Tiago Valente

Reputation: 33

If I understood you correctly, you can simulate the effects of the parent being hidden using CSS like this.

HTML

<div class="outer hide">
    <div id="inner1" class="inner hide">Inner 1</div>
    <div id="inner2" class="inner">Inner 2</div>
    <div id="inner3" class="inner">Inner 3</div>
</div>

CSS

.hide {
    background: rgba(0,0,0,0);
    color: rgba(0,0,0,0);
    border-color: rgba(0,0,0,0);

    // For an SVG
    fill: rgba(0,0,0,0);
    stroke-opacity: 0;
}

The reason why you can't use the visibility/display/opacity property is because as @Umesh mentioned that the all descendant elements will also get their display/visibility/opacity as not visible as if the element doesn't exist in the document tree.

But using this method you set the alpha to 0 of the element and this doesn't effect the descendants unless they have inherit set for those properties.

Hope this helps.

Upvotes: 1

Pragya Jain
Pragya Jain

Reputation: 31

Take a flag value and loop each div.inner to get first visible element. Then check its css property.

Below is the tested code :

var isValid=true;
$("div.inner").each(function() {
    if($(this).css("display") == "block" && isValid) {
        $("div#result").text($(this).attr('id'));isValid=false;
    }
});

Upvotes: 0

Umesh
Umesh

Reputation: 29

MDN says:

When you use none, all descendant elements also have their display turned off. The document is rendered as though the element doesn't exist in the document tree.

Hence, whatever the HTML elements are child to the parent element will not be rendered in the HTML page.

And moreover, whatever styles that has been applied on the parent element will not be rendered in HTML page.

In order to achieve what you want and if you consider that your HTML element should be in the document tree then try using CSS visibility property. For example:

<div class="outer" style="visibility: hidden">
  <div id="inner1" class="inner" style="display: none"></div>
  <div id="inner2" class="inner" style="visibility: visible"></div>
  <div id="inner3" class="inner"></div>
</div>

JS Fiddle

Upvotes: 1

Vijay Maheriya
Vijay Maheriya

Reputation: 1679

See here i have checked style attribute for ignore first div. And check with hidden selector for get all other div.

$(document).ready(function(){
  var currElements=$('.inner[style!="display: none"]:hidden'); // Here you are get two div with id inner2 and inner3
  alert(currElements[0].id); // First div
  alert(currElements[1].id); // First div
  });
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="outer" style="display: none">
  <div id="inner1" class="inner" style="display: none">Inner 1</div>
  <div id="inner2" class="inner">Inner 2</div>
  <div id="inner3" class="inner">Inner 3</div>
</div>

Upvotes: 0

gaetanoM
gaetanoM

Reputation: 42054

My proposal is to use a filter function but only to select the first visible element (but this is also hidden because the parent is hidden):

var $first_visible = $('div.inner').filter(function() {
   return !(this.style.visibility != '' || this.style.display != '');
}).first();

$(function () {
  var $first_visible = $('div.inner').filter(function() {
    return !(this.style.visibility != '' || this.style.display != '');
  }).first();
  $('body').append('<p>' + $first_visible.attr('id') + '</p>');
});
<script src="http://code.jquery.com/jquery-1.11.3.js"></script>


<div class="outer" style="display: none">
    <div id="inner1" class="inner" style="display: none;"></div>
    <div id="inner2" class="inner"></div>
    <div id="inner3" class="inner"></div>
</div>

Upvotes: 0

Josh Crozier
Josh Crozier

Reputation: 240968

One option would be to show the parent element, check for the first visible element, and then hide the parent element again.

Alternatively, since the element has inline CSS, you could filter the elements based on whether the display property is set to none and then retrieve the first one in the filtered collection:

Updated Example

var $first_visible = $(".inner").filter(function () {
  return this.style.display !== 'none';
}).first();

var $first_visible = $(".inner").filter(function () {
  return this.style.display !== 'none';
}).first();

$("div#result").text('First visible: #' + $first_visible[0].id);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="outer" style="display: none;">
  <div id="inner1" class="inner" style="display: none"></div>
  <div id="inner2" class="inner"></div>
  <div id="inner3" class="inner"></div>
</div>

<div id="result"></div>


However, the better approach would be to check the computed style of the element using the .getComputedStyle() method. In doing so, you can determine whether the display of the element is set to none even if the element doesn't have inline CSS.

Updated Example

var $first_visible = $(".inner").filter(function () {
  return window.getComputedStyle(this, null).getPropertyValue('display') !== 'none';
}).first();

var $first_visible = $(".inner").filter(function () {
  return window.getComputedStyle(this, null).getPropertyValue('display') !== 'none';
}).first();

$("div#result").text('First visible: #' + $first_visible[0].id);
#inner1 { display: none; }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="outer" style="display: none;">
  <div id="inner1" class="inner"></div>
  <div id="inner2" class="inner"></div>
  <div id="inner3" class="inner"></div>
</div>

<div id="result"></div>

Upvotes: 0

GameTag
GameTag

Reputation: 379

write two classes : first one to display and last one to hide. With that you can select all divs whoses "visible" even if parent is "hidden"

var $first_visible = $("div.inner.enable");
console.log($first_visible);
$("div#result").text($first_visible[0].id);
.disable{
  display : none;
}

.enable{
  display : block;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="outer disable">
  <div id="inner1" class="inner disable">1</div>
  <div id="inner2" class="inner enable">2</div>
  <div id="inner3" class="inner enable">3</div>
</div>

<div id="result"></div>

Upvotes: 0

AmmarCSE
AmmarCSE

Reputation: 30557

As adeneo said, once its hidden, there isnt much you can do.

However, you can check before hand, show it regardless, then hide it again if it was hidden

var wasVisible = $(".outer").is(':visible');

$(".outer").show();

var $first_visible = $("div.inner:visible:first");

if (!wasVisible) {
  $(".outer").hide();
}

console.log($first_visible.length);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<div class="outer" style="display: none">
  <div id="inner1" class="inner" style="display: none"></div>
  <div id="inner2" class="inner"></div>
  <div id="inner3" class="inner"></div>
</div>

Upvotes: 1

Related Questions