jae
jae

Reputation: 13

hover over one div to show the nearest div of a certain class

i want to hover over one div and use jquery to find the nearest div by the name and to show that div.

<div class="entry">
  <div class="body"></div>
  <div class="date"></div>
  <div class="footer"></div>

  <div class="body"></div>
  <div class="date"></div>
  <div class="footer"></div>

  <div class="body"></div>
  <div class="somethingelse"></div>
  <div class="footer"></div>
</div>

all the .footer classes will be hidden but i want to make it so that when i over over the .body class, only the nearest .footer class shows. [ meaning : if i hover over the first .body class, only the first .footer will be shown. ]

my current code isn't working and i'm starting to wonder if it's something wrong with it.

current jquery code :

$('.footer').hide();
$('.body').hover(function(){
     $(this).closest('.footer').find('.footer').show();
});

Upvotes: 0

Views: 1754

Answers (5)

Racil Hilan
Racil Hilan

Reputation: 25351

The answer by freedomn-m offered a good explanation and good solution in case you want the nearest NEXT .footer, which seems to be the case from your example HTML.

However, if you want your request strictly, so you want exact NEAREST .footer, then his solution will not work for you. And I don't think there is a jQuery built-in functionality that can give you that, so you'll have to do it manually. Get the list of the children of the parent (don't use the siblings as they don't include the current element) and go through the list to calculate the distance from your current element using the indexes and then select the .footer that is really the nearest.

$('.body').hover(function() {
  var children = $(this).parent().children();
  var index = children.index(this);
  var closest = children.length;
  var footer = -1;
  children.each(function(i, child) {
    if (i !== index && $(child).hasClass("footer")) {
      var distance = Math.abs(index - i);
      if (distance < closest) {
        closest = distance;
        footer = i;
      }
    }
  });
  if (footer > -1)
    children.eq(footer).show();
}, function() {
  $(".footer").hide();
});
body {
  font: 13px Verdana;
}

.footer {
  display: none;
  color: red;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="entry">
  <div class="body">body</div>
  <div class="date">date</div>
  <div class="footer">footer</div>

  <div class="body">body</div>
  <div class="date">date</div>
  <div class="footer">footer</div>

  <div class="body">body</div>
  <div class="somethingelse">somethingelse</div>
  <div class="footer">footer</div>
</div>

If you don't care much about the performance, you can shorten the code a bit by selecting the list of .footer instead of the children of the parent, and then let jQuery give you the index of each of them. Not very efficient, but shorter code:

$('.body').hover(function() {
  var index = $(this).index();
  var closest = 9999;
  var footer;
  $(this).siblings(".footer").each(function(i, sibling) {
    var distance = Math.abs(index - $(sibling).index());
    if (distance < closest) {
      closest = distance;
      footer = sibling;
    }
  });
  if (footer !== undefined)
    $(footer).show();
}, function() {
  $(".footer").hide();
});
body {
  font: 13px Verdana;
}

.footer {
  display: none;
  color: red;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="entry">
  <div class="body">body</div>
  <div class="date">date</div>
  <div class="footer">footer</div>

  <div class="body">body</div>
  <div class="date">date</div>
  <div class="footer">footer</div>

  <div class="body">body</div>
  <div class="somethingelse">somethingelse</div>
  <div class="footer">footer</div>
</div>

Inspired by freedomn-m's comment, we can also use the .prevAll() and .nextAll() methods to get the previous and next .footer siblings. These two methords return the siblings ordered by the closest, so we simply pick the first one of each list, subtract their indexes from our element's index (to find the distance), compare them together, and return the closest. This solution is also less efficient than the first one, but you may find the code easier to read:

$('.body').hover(function() {
  var me = $(this);
  var prev = me.prevAll(".footer").first();
  var next = me.nextAll(".footer").first();

  if (prev.length == 0)
    next.show();
  else if (next.length == 0)
    prev.show();
  else {
    index = me.index();
    if (Math.abs(prev.index() - index) < Math.abs(next.index() - index))
      prev.show();
    else
      next.show();
  }
}, function() {
  $(".footer").hide();
});
body {
  font: 13px Verdana;
}

.footer {
  display: none;
  color: red;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="entry">
  <div class="body">body</div>
  <div class="date">date</div>
  <div class="footer">footer</div>

  <div class="body">body</div>
  <div class="date">date</div>
  <div class="footer">footer</div>

  <div class="body">body</div>
  <div class="somethingelse">somethingelse</div>
  <div class="footer">footer</div>
</div>

Upvotes: 0

Facundo Corradini
Facundo Corradini

Reputation: 3923

IF your html is gonna keep those triads layout, you don't need jQuery for it. Just use CSS to select the second div after the .body on hover

div{width:100px; height:100px; background-color:lime; margin:10px; float:left}

.body{background:yellow; clear:left;}
.footer{display:none;}

.body:hover + div + div{
  display:block;
  background:red;
}
<div class="body"></div>
  <div class="date"></div>
  <div class="footer"></div>

  <div class="body"></div>
  <div class="date"></div>
  <div class="footer"></div>

  <div class="body"></div>
  <div class="somethingelse"></div>
  <div class="footer"></div>

Upvotes: 0

fdomn-m
fdomn-m

Reputation: 28611

While the problem is the same as this question, the reason is slightly different.

When you use .closest(".class") it's the equivalent of .parents().filter(".class").first() (or .last(), I don't recall exactly which way parents() works as that's what closest is for).

ie it goes up the tree

So $(".body").closest(".entry") would give you an element for your HTML.

In this case, you want siblings, but more specifically the next one. There's a jquery method .next() which looks like it's correct, but as detailed in the link above, this only gives the very next one (in your HTML this would be the date div) even if a filter is applied - so $(this).next(".footer") would give an empty set (as it's not .date).

The work around is:

$(this).nextAll(".footer").first()

Once you get this working, your will find that your hover does not work as expected as the footers are not hiding again - as you're using .hover rather than mouseenter mouseout, you just need to move the .hide() call inside the second event handler, giving:

// startup
$(".footer").hide();

// event
$(".body").hover(function() {
    $(this).nextAll(".footer").first().show();
}, function() {
    $(".footer").hide();
});
div > div { width: 100px; height: 10px }
.body { border: 1px solid red; }
.date { border: 1px solid blue; }
.footer { border: 1px solid green; }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="entry">
  <div class="body"></div>
  <div class="date"></div>
  <div class="footer"></div>

  <div class="body"></div>
  <div class="date"></div>
  <div class="footer"></div>

  <div class="body"></div>
  <div class="somethingelse"></div>
  <div class="footer"></div>
</div>

Upvotes: 3

Bhuwan
Bhuwan

Reputation: 16855

Try to make use of nextUntil(".footer").next(); as below

$('.body').hover(function() {
  $(this).nextUntil(".footer").next().show();
}, function() {
  $(".footer").hide();
});
body {
  font: 13px Verdana;
}

.footer {
  display: none;
  color: red;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="entry">
  <div class="body">body</div>
  <div class="date">date</div>
  <div class="footer">footer</div>

  <div class="body">body</div>
  <div class="date">date</div>
  <div class="footer">footer</div>

  <div class="body">body</div>
  <div class="somethingelse">somethingelse</div>
  <div class="footer">footer</div>
</div>

Upvotes: 0

Alex Andre
Alex Andre

Reputation: 1036

$(this).closest('.footer')

You should start to use console.log() sometimes to check elements you would like to get. This does not find anything so nothing further to search and to show.

If you possibly can separate bodies and footers into containers you can do smth like this.

Upvotes: 0

Related Questions