How do I DRY up this script?

2019-09-18 09:45发布

I have been plugging away at this navigation code, and I finally got the 1st layers of my menu to work. Yay! But I've noticed that my code ONLY works on the 1st layers. Well, kinda. Open menus are supposed to close when you click on a different menu. It works for my top-level nav options, but not my sub-menu options. I'm tempted to just copy/paste the code so that everything works for all levels, but I know one big rule in coding is Don't Repeat Yourself (DRY). So could someone have a look at this and see if there's a way I could apply the rules of the 1st If/Else statement to all my submenus? (sub-menu, drop-menu, slide-menu).

Also, if my top-level nav starts to wrap, when you click on one of the links on the 2nd line, the sub-menu will display OVER the top-level link, making it almost impossible to close. Is there a way to fix that?

Here is my jsFiddle

jQuery

$(document).ready(function(){ 

$(".nav-tabs span").click(function(){



            var activeTab = $(".nav-tabs > li span.open");
            var submenu = $(this).siblings("ul");
            var thisParent = $(this).closest("ul");

            if (thisParent.hasClass("nav-tabs")){

                if (!$(this).is(activeTab)){
                    /*
                    alert("this link was not active yet");
                    */
                    activeTab.siblings("ul").slideUp(); 
                    submenu.find("span+ul").hide();
                    activeTab.removeClass("open");
                    $(this).addClass("open");
                    submenu.slideDown();

                } else {
                    /*
                    alert("this link is already active");
                    */
                    submenu.slideUp();
                    submenu.find("span+ul").hide();
                    $(this).removeClass("open");
                }
            } else {
                $(this).toggleClass("open");
                submenu.slideToggle("fast", function(){
                    if (!$(this).is(".open")){
                        submenu.find("span+ul").removeClass("open").hide();
                    }
                });

            }


        });

        });

HTML

<div id="navbar">
    <ul class="nav-tabs">
        <li><span>Home</span></li>
        <li id="active"><span>Dogs <div class="arrow-down"></div></span>
            <ul class="sub-menu">
                <li><span>Meet the Breeds<div class="arrow-down"></div></span>
                    <ul class="drop-menu">
                        <li><span>Sort A - Z ~ </span>
                            <ul class="slide-menu">
                                <li>Breeds A - F</li>
                                <li>Breeds G - L</li>
                                <li>Breeds M - R</li>
                                <li>Breeds S - Z</li>
                            </ul>
                        </li>
                        <li><span>Sort by AKC Group ~</span>
                            <ul class="slide-menu">
                                <li>Sporting Group</li>
                                <li>Working Group</li>
                                <li>Herding Group</li>
                                <li>Hound Group</li>
                                <li>Terrier Group</li>
                                <li>Non-Sporting Group</li>
                                <li>Toy Group</li>
                            </ul>
                        </li>
                        <li><span>Sort by Size ~</span>
                            <ul class="slide-menu">
                                <li>X-Small (&le 10in)</li>
                                <li>Small (10in &gt &lt 15in)</li>
                                <li>Medium (15in &ge &lt 21in)</li>
                                <li>Large (21in &ge &lt 28in)</li>
                                <li>X-Large (28in +)</li>
                            </ul>
                        </li>
                        <li><span>Sort by Coat ~</span>
                            <ul class="slide-menu">
                                <li>Very Short/Hairless</li>
                                <li>Short Coat</li>
                                <li>Medium Coats</li>
                                <li>Long Coats</li>
                                <li>Non-Shedding Coats</li>
                                <li>Curly Coats</li>
                            </ul>
                        </li>
                        <li><span>Sort by Trait ~</span>
                            <ul class="slide-menu">
                                <li>Apartment Suitable</li>
                                <li>Laid Back</li>
                                <li>Athletic</li>
                                <li>Protective</li>
                                <li>Extroverted</li>
                                <li>Pet Friendly</li>
                                <li>Cuddle-Buddies</li>
                            </ul>
                        </li>
                    </ul>
                </li>
                <li><span>Supplies<div class="arrow-down"></div></span>
                    <ul class="drop-menu">
                        <li><span>Crates & Kennels</li>
                        <li><span>Bowls & Dishes</li>
                        <li><span>Collars & Leashes</li>
                        <li><span>Toys & Games</li>
                        <li><span>Grooming</li>
                        <li><span>Apparal & Accessories</li>
                    </ul>
                </li>
                <li><span>Finding a Dog<div class="arrow-down"></div></span></li>
            </ul>

        </li>
        <li><span>Cats<div class="arrow-down"></div></span>
            <ul class="sub-menu">
                <li><span>Cat Links<div class="arrow-down"></div></span></li>
                <li><span>Cat Links<div class="arrow-down"></div></span></li>
                <li><span>Cat Links<div class="arrow-down"></div></span></li>
                <li><span>Cat Links<div class="arrow-down"></div></span></li>
                <li><span>Cat Links<div class="arrow-down"></div></span></li>
                <li><span>Cat Links<div class="arrow-down"></div></span></li>
            </ul>
        </li>
        <li><span>Birds<div class="arrow-down"></div></span>
            <ul class="sub-menu">
                <li><span>Bird Links<div class="arrow-down"></div></span></li>
                <li><span>Bird Links<div class="arrow-down"></div></span></li>
                <li><span>Bird Links<div class="arrow-down"></div></span></li>
                <li><span>Bird Links<div class="arrow-down"></div></span></li>
                <li><span>Bird Links<div class="arrow-down"></div></span></li>
                <li><span>Bird Links<div class="arrow-down"></div></span></li>
            </ul>
        </li>
        <li><span>Small Mammals<div class="arrow-down"></div></span>
            <ul class="sub-menu">
                <li><span>Sm.Mammal Links<div class="arrow-down"></div></span></li>
                <li><span>Sm.Mammal Links<div class="arrow-down"></div></span></li>
                <li><span>Sm.Mammal Links<div class="arrow-down"></div></span></li>
                <li><span>Sm.Mammal Links<div class="arrow-down"></div></span></li>
                <li><span>Sm.Mammal Links<div class="arrow-down"></div></span></li>
                <li><span>Sm.Mammal Links<div class="arrow-down"></div></span></li>
            </ul>
        </li>
        <li><span>Articles<div class="arrow-down"></div></span>
            <ul class="sub-menu">
                <li><span>Article Links<div class="arrow-down"></div></span></li>
                <li><span>Article Links<div class="arrow-down"></div></span></li>
                <li><span>Article Links<div class="arrow-down"></div></span></li>
                <li><span>Article Links<div class="arrow-down"></div></span></li>
                <li><span>Article Links<div class="arrow-down"></div></span></li>
                <li><span>Article Links<div class="arrow-down"></div></span></li>
            </ul>
        </li>
        <li><span>Videos<div class="arrow-down"></div></span>
            <ul class="sub-menu">
                <li><span>Video Links<div class="arrow-down"></div></span></li>
                <li><span>Video Links<div class="arrow-down"></div></span></li>
                <li><span>Video Links<div class="arrow-down"></div></span></li>
                <li><span>Video Links<div class="arrow-down"></div></span></li>
                <li><span>Video Links<div class="arrow-down"></div></span></li>
                <li><span>Video Links<div class="arrow-down"></div></span></li>
            </ul>
        </li>
        <li><span>Updates<div class="arrow-down"></div></span>
            <ul class="sub-menu">
                <li><span>More Links<div class="arrow-down"></div></span></li>
                <li><span>More Links<div class="arrow-down"></div></span></li>
                <li><span>More Links<div class="arrow-down"></div></span></li>
                <li><span>More Links<div class="arrow-down"></div></span></li>
                <li><span>More Links<div class="arrow-down"></div></span></li>
                <li><span>More Links<div class="arrow-down"></div></span></li>
            </ul>
        </li>
    </ul>
</div>

CSS included on jsFiddle

2条回答
别忘想泡老子
2楼-- · 2019-09-18 09:54

Your looking for $(this).offset(); it tells you the position of the element relative to the document, so you will need to subtract the position of the nav-bar to get the TOP position of the element. Since you want to display the menu below the clicked element you will also have to add the height of the clicked element to the TOP position to push it below. See this snippet for working and commented code

EDIT: this is an answer to part of the question(posed by OP in comments) posted as an answer so I could add the runnable snippet. Also collapsed code snippet so it doesn't take up so much space.

$(document).ready(function() {

  $(".nav-tabs span").click(function() {
    var activeTab = $(".nav-tabs > li span.open");
    var submenu = $(this).siblings("ul");
    var thisParent = $(this).closest("ul");
    /*calculate the offset*/
    var offset = $(this).offset(); //gets the offset relative to document
    var parentOffset = $('#navbar').offset(); //get the navbar offset relative to doc
    var heightOfClickedElement = $(this).outerHeight();
    var relativeOffsetTop = offset.top - parentOffset.top + heightOfClickedElement;
    $('.sub-menu').css('top', relativeOffsetTop + 'px');
    /*done calculating the offset */
    if (thisParent.hasClass("nav-tabs")) {

      if (!$(this).is(activeTab)) {
        /*
					alert("this link was not active yet");
					*/
        activeTab.siblings("ul").slideUp();
        submenu.find("span+ul").hide();
        activeTab.removeClass("open");
        $(this).addClass("open");
        submenu.slideDown();

      } else {
        /*
					alert("this link is already active");
					*/
        submenu.slideUp();
        submenu.find("span+ul").hide();
        $(this).removeClass("open");
      }
    } else {
      $(this).toggleClass("open");
      submenu.slideToggle("fast", function() {
        if (!$(this).is(".open")) {
          submenu.find("span+ul").removeClass("open").hide();
        }
      });

    }


  });

});
#navbar {
  display: block;
  clear: both;
  width: 100%;
  height: auto;
  margin: 0px;
  padding: 0%;
  background-color: #29568F;
  border-bottom: 3px solid #29568F;
}
.nav-tabs {
  display: inline-block;
  position: relative;
  width: 100%;
  background: #29568F;
  margin: 0px 0px;
  padding: 0px;
  list-style-type: none;
  color: white;
  text-decoration: none;
  text-shadow: 2px 2px #000000;
  font: 18px arial, verdana, sans-serif;
}
.nav-tabs li {
  cursor: pointer;
  float: left;
  padding: 10px 20px;
  text-align: center;
  border-right: 1px solid lightgrey;
}
.nav-tabs li:last-child {
  border: 0px;
}
.nav-tabs li:hover {
  background-color: #3399CC;
}
#active {
  background-color: #3399CC;
}
.nav-tabs li a {
  color: white;
  text-decoration: none;
  text-shadow: 2px 2px #000000;
  font: 18px arial, verdana, sans-serif;
}
/*
	.arrow-down {
		display: inline-block;
		float: right;
		margin: 8px 0px 0px 8px;
		content: url("/images/arrow-down.png");
	}*/

<----------Horizontal Sub-Menu-----------------------> .nav-tabs li .sub-menu {
  display: none;
}
/*
	.nav-tabs li:hover .sub-menu {
		display: block;
	}*/

.sub-menu {
  display: none;
  z-index: 200;
  width: 100%;
  background-color: #3399CC;
  position: absolute;
  top: 41px;
  left: 0px;
  padding: 0px;
  border-bottom: 3px solid #29568F;
}
.sub-menu li {
  list-style-type: none;
  position: relative;
  border: 0px;
  left: 5%;
}
.sub-menu li:hover {
  background-color: #C9EAF3;
}
.sub-menu li:hover a {
  color: #000000;
  text-shadow: 2px 2px #ffffff;
}
<---------------Drop Down Sub-Menu----------------------> .sub-menu li .drop-menu {
  display: none;
}
/*
	.sub-menu li:hover .drop-menu {
		display: block;
	}*/

.drop-menu {
  display: none;
  position: absolute;
  top: 100000px;
  left: -3px;
  margin: 0px;
  padding: 0px;
  background-color: #C9EAF3;
  /*
		width: 120%;*/
  min-width: 220px;
  z-index: 200;
}
.drop-menu li {
  border-bottom: 1px solid #ffffff;
  border-right: 3px solid #29568F;
  border-left: 3px solid #29568F;
  list-style-type: none;
  position: relative;
  left: 0px;
  margin: 0px;
  padding: 10px 1%;
  width: 95%;
  text-indent: 10px;
  text-align: left;
  text-shadow: none !important;
  color: black;
  font-size: 15px;
}
.drop-menu li a {
  text-shadow: none !important;
  color: black;
  font-size: 15px;
}
.drop-menu li:last-child {
  border-bottom: 3px solid #29568F;
  border-right: 3px solid #29568F;
  border-left: 3px solid #29568F;
}
.drop-menu li:hover {
  background-color: #ffffff;
}
<-----------------Slide Out Sub-Menu------------------------> .drop-menu li .slide-menu {
  display: none;
}
/*
	.drop-menu li:hover .slide-menu {
		display: block;
	}*/

.slide-menu {
  display: none;
  position: absolute;
  top: 0px;
  left: 213px;
  margin: 0px;
  padding: 0px;
  width: 120%;
  background-color: white;
  border-top: 3px solid #29568F;
}
.slide-menu:first-child {
  border-top: 0px;
}
.slide-menu li {
  border-right: 3px solid #29568F;
  border-left: 3px solid #29568F;
  list-style-type: none;
  margin: 0px;
  padding: 10px 1%;
  width: 96%;
  text-indent: 10px;
}
.slide-menu li:first-child {
  border-left: 0px;
  position: relative;
  left: 3px;
}
.slide-menu li:hover a {
  text-decoration: underline;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="navbar">
  <ul class="nav-tabs">
    <li><span>Home</span>
    </li>
    <li id="active"><span>Dogs <div class="arrow-down"></div></span>

      <ul class="sub-menu">
        <li><span>Meet the Breeds<div class="arrow-down"></div></span>

          <ul class="drop-menu">
            <li><span>Sort A - Z ~ </span>

              <ul class="slide-menu">
                <li>Breeds A - F</li>
                <li>Breeds G - L</li>
                <li>Breeds M - R</li>
                <li>Breeds S - Z</li>
              </ul>
            </li>
            <li><span>Sort by AKC Group ~</span>

              <ul class="slide-menu">
                <li>Sporting Group</li>
                <li>Working Group</li>
                <li>Herding Group</li>
                <li>Hound Group</li>
                <li>Terrier Group</li>
                <li>Non-Sporting Group</li>
                <li>Toy Group</li>
              </ul>
            </li>
            <li><span>Sort by Size ~</span>

              <ul class="slide-menu">
                <li>X-Small (&le 10in)</li>
                <li>Small (10in &gt &lt 15in)</li>
                <li>Medium (15in &ge &lt 21in)</li>
                <li>Large (21in &ge &lt 28in)</li>
                <li>X-Large (28in +)</li>
              </ul>
            </li>
            <li><span>Sort by Coat ~</span>

              <ul class="slide-menu">
                <li>Very Short/Hairless</li>
                <li>Short Coat</li>
                <li>Medium Coats</li>
                <li>Long Coats</li>
                <li>Non-Shedding Coats</li>
                <li>Curly Coats</li>
              </ul>
            </li>
            <li><span>Sort by Trait ~</span>

              <ul class="slide-menu">
                <li>Apartment Suitable</li>
                <li>Laid Back</li>
                <li>Athletic</li>
                <li>Protective</li>
                <li>Extroverted</li>
                <li>Pet Friendly</li>
                <li>Cuddle-Buddies</li>
              </ul>
            </li>
          </ul>
        </li>
        <li><span>Supplies<div class="arrow-down"></div></span>

          <ul class="drop-menu">
            <li><span>Crates & Kennels</li>
                        <li><span>Bowls & Dishes</li>
                        <li><span>Collars & Leashes</li>
                        <li><span>Toys & Games</li>
                        <li><span>Grooming</li>
                        <li><span>Apparal & Accessories</li>
                    </ul>
                </li>
                <li><span>Finding a Dog<div class="arrow-down"></div></span>
            </li>
          </ul>
        </li>
        <li><span>Cats<div class="arrow-down"></div></span>

          <ul class="sub-menu">
            <li><span>Cat Links<div class="arrow-down"></div></span>
            </li>
            <li><span>Cat Links<div class="arrow-down"></div></span>
            </li>
            <li><span>Cat Links<div class="arrow-down"></div></span>
            </li>
            <li><span>Cat Links<div class="arrow-down"></div></span>
            </li>
            <li><span>Cat Links<div class="arrow-down"></div></span>
            </li>
            <li><span>Cat Links<div class="arrow-down"></div></span>
            </li>
          </ul>
        </li>
        <li><span>Birds<div class="arrow-down"></div></span>

          <ul class="sub-menu">
            <li><span>Bird Links<div class="arrow-down"></div></span>
            </li>
            <li><span>Bird Links<div class="arrow-down"></div></span>
            </li>
            <li><span>Bird Links<div class="arrow-down"></div></span>
            </li>
            <li><span>Bird Links<div class="arrow-down"></div></span>
            </li>
            <li><span>Bird Links<div class="arrow-down"></div></span>
            </li>
            <li><span>Bird Links<div class="arrow-down"></div></span>
            </li>
          </ul>
        </li>
        <li><span>Small Mammals<div class="arrow-down"></div></span>

          <ul class="sub-menu">
            <li><span>Sm.Mammal Links<div class="arrow-down"></div></span>
            </li>
            <li><span>Sm.Mammal Links<div class="arrow-down"></div></span>
            </li>
            <li><span>Sm.Mammal Links<div class="arrow-down"></div></span>
            </li>
            <li><span>Sm.Mammal Links<div class="arrow-down"></div></span>
            </li>
            <li><span>Sm.Mammal Links<div class="arrow-down"></div></span>
            </li>
            <li>
              <="#" <span>More Links
                <div class="arrow-down"></div>
                </span>
            </li>
          </ul>
        </li>
        <li><span>Articles<div class="arrow-down"></div></span>

          <ul class="sub-menu">
            <li><span>Article Links<div class="arrow-down"></div></span>
            </li>
            <li><span>Article Links<div class="arrow-down"></div></span>
            </li>
            <li><span>Article Links<div class="arrow-down"></div></span>
            </li>
            <li><span>Article Links<div class="arrow-down"></div></span>
            </li>
            <li><span>Article Links<div class="arrow-down"></div></span>
            </li>
            <li><span>Article Links<div class="arrow-down"></div></span>
            </li>
          </ul>
        </li>
        <li><span>Videos<div class="arrow-down"></div></span>

          <ul class="sub-menu">
            <li><span>Video Links<div class="arrow-down"></div></span>
            </li>
            <li><span>Video Links<div class="arrow-down"></div></span>
            </li>
            <li><span>Video Links<div class="arrow-down"></div></span>
            </li>
            <li><span>Video Links<div class="arrow-down"></div></span>
            </li>
            <li><span>Video Links<div class="arrow-down"></div></span>
            </li>
            <li><span>Video Links<div class="arrow-down"></div></span>
            </li>
          </ul>
        </li>
        <li><span>Updates<div class="arrow-down"></div></span>

          <ul class="sub-menu">
            <li><span>More Links<div class="arrow-down"></div></span>
            </li>
            <li><span>More Links<div class="arrow-down"></div></span>
            </li>
            <li><span>More Links<div class="arrow-down"></div></span>
            </li>
            <li><span>More Links<div class="arrow-down"></div></span>
            </li>
            <li><span>More Links<div class="arrow-down"></div></span>
            </li>
            <li><span>More Links<div class="arrow-down"></div></span>
            </li>
          </ul>
        </li>
      </ul>
</div>

查看更多
smile是对你的礼貌
3楼-- · 2019-09-18 10:17

Ha! I was able to figure out an answer to this on my own!

Where I have the variables defined here:

var activeTab = $(".nav-tabs > li span.open");
var submenu = $(this).siblings("ul");
var thisParent = $(this).closest("ul");

All I had to do was change the activeTab variable to this:

var thisParent = $(this).closest("ul");
var activeTab = thisParent.children().children("span.open");
var submenu = $(this).siblings("ul");

Works perfectly! And now this code works perfectly for all menus and I don't have to repeat myself!

查看更多
登录 后发表回答