1

I'm having a bit of trouble figuring out why my code is not working. In the below example html I have three nav-tab elements. I'm trying to target the second element (Products tab) in order to make the following changes:

  1. Add a red border to the div.nav-tab element on hover (Products tab only)
  2. Change the text of "Products" to "Merchandise"

I know this can easily be done by changing the CSS and HTML but i'm trying to do this using only jQuery.

In my script I am trying to create a jQuery variable and then use the .hover and .find methods on it, but I always get the error ".find -or- .hover is not a function".

Additionally, I may want to add more tabs to the beginning of the navbar at a later time, so i don't want to use something like a class selector and target the second element, because when i add more tabs later, this will end up not targeting the Products tab. e.g...

$( ".nav-tab" )[1].hover...

I'm sure there's an easy way to do this. Could someone please give some advice as to how best to make the changes with just using jQuery? Thanks in advance!

I've created an example fiddle

<nav>
    <div class="nav-tab">
        <a class="nav-link" data-name="About">
            <div class="item-menu">
                <span>About</span>
            </div>
        </a>
    </div>
    <div class="nav-tab">
        <a class="nav-link" data-name="Products">
            <div class="item-menu">
                <span>Products</span>
            </div>
        </a>
    </div>
    <div class="nav-tab">
        <a class="nav-link" data-name="Contact">
            <div class="item-menu">
                <span>Contact</span>
            </div>
        </a>
    </div>
</nav>

<script>
$( document ).ready(function() {
    let products = $( ".nav-tab" ).has( '[data-name="Products"]' ).get( 0 );
    products.hover(
        function() {$(this).addClass("red-border")},
        function() {$(this).removeClass("red-border")}
    );
    products.find('span').innerText="Merchandise";
});
</script>
Arif
  • 6,094
  • 4
  • 49
  • 81
James Bradley
  • 125
  • 3
  • 12

4 Answers4

1

When you are using $(".nav-tab")[1] you are escaping jQuery's wrapper around the object, that's why it won't allow to use jQuery related items, not tested but you can try fixing that by wrapping it again like $($(".nav-tab")[1]).hover(), but that is not the way you should do this though.

Best way would be to use a combination of the CSS selector and jQuery's .parent() function, like this:

let products = $("[data-name='Products']").parent();

This way you are simply selecting the first parent of the above found element.

Can also add a specific selector above to make it like this (not required though):

let products = $("[data-name='Products']").parent(".nav-tab");

https://api.jquery.com/parent/

And for the text changes just use jQuery's .text() function:

products.find("span").text("Merchandise");

https://api.jquery.com/text/

Also can check out this for way to select the first object within many with jQuery https://api.jquery.com/first/

EDIT: Since you are getting 2nd object, the above .first() is not relevant, .get() is what you want, but that is still not the way you should be doing it, because as you mentioned if you append another nav-tab then it will ruin everything.

ExacT
  • 49
  • 1
  • 8
1

Dereferencing jQuery Objects

When you use ....get(0) (or ...[0]) you are dereferencing the jQuery Object, thereby changing it into a plain JavaScript Object. Neither object can be "seen" by the other's methods. Do not use .get(0), just storing a jQuery Object in a variable alone usually suffices.

IMO .hover() method is problematic and is easily replaced by CSS pseudo-class :hover. As a jQuery solution the .on() method with the mouseenter and mouseleave events is better so that you can handle each event separately. .innerText is a plain JavaScript property which does not recognize jQuery Objects, use jQuery method .text() instead.

Also, instead of CSS border property, use outline instead. outline doesn't add it's length while border does thereby causing neighboring elements to shift.


Demo

Details commented in demo.

$(document).ready(function() {
  // Reference as a jQuery Object
  const products = $('[data-name="Products"]');
  // Register both events on element...
  products.on('mouseenter mouseleave', function(event) {
    // if the type of event is 'mouseenter...
    if (event.type === 'mouseenter') {
      // Get it's descendant <span>, change it's text, then add .red-border to it
      $(this).find('span').text("Merchandise").addClass("red-border");
      // Otherwise do the opposite
    } else {
      $(this).find('span').text("Product").removeClass("red-border");
    }
  });
});
.red-border {
  outline: 3px dashed red
}
<nav>
  <div class="nav-tab">
    <a class="nav-link" data-name="About">
      <div class="item-menu">
        <span>About</span>
      </div>
    </a>
  </div>
  <div class="nav-tab">
    <a class="nav-link" data-name="Products">
      <div class="item-menu">
        <span>Products</span>
      </div>
    </a>
  </div>
  <div class="nav-tab">
    <a class="nav-link" data-name="Contact">
      <div class="item-menu">
        <span>Contact</span>
      </div>
    </a>
  </div>
</nav>

<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
Community
  • 1
  • 1
zer00ne
  • 41,936
  • 6
  • 41
  • 68
0

Use this code:

$( document ).ready(function() {
    var products = $( ".nav-tab").find("[data-name='Products']");
  products
  .mouseover(function(){
    $(this).find("span").text("Merchandise").closest(".nav-tab").addClass("red-border");
  })
  .mouseout(function(){
    $(this).parent(".nav-tab").removeClass("red-border");
    //Again if you want to replace "Merchandise" to "Product" then active this commanded line
    //$(this).closest(".nav-tab").removeClass("red-border").find("span").text("Products");
  });
});

I updated your code in fiddler: https://jsfiddle.net/Arif2009/gksoynLq/31/

Arif
  • 6,094
  • 4
  • 49
  • 81
-1

Try this:

$( document ).ready(function() {
var products =  $(".nav-tab a[data-name=Products]");
  $(products).hover(function(){
    $(this).addClass('red-border');
    }, function(){
    $(this).removeClass('red-border');
  });
  $('span',products)[0].innerText="Merchandise";
});
Ali Adlavaran
  • 3,697
  • 2
  • 23
  • 47
  • Code-only answers are not useful. What did you do? Why does it work? Consider adding some comments / description / explanation of what you've done and why. – random_user_name Feb 10 '19 at 02:13