我如何干燥这个脚本?

How do I DRY up this script?

我一直在研究这个导航代码,我终于让我的菜单的第一层工作了。耶!但我注意到我的代码只适用于第一层。好吧,有点。当您单击不同的菜单时,打开的菜单应该会关闭。它适用于我的顶级导航选项,但不适用于我的子菜单选项。我很想只 copy/paste 代码,以便所有级别都适用,但我知道编码中的一个重要规则是不要重复自己 (DRY)。那么有人可以看看这个,看看是否有办法将第一个 If/Else 语句的规则应用于我的所有子菜单? (子菜单、下拉菜单、滑动菜单)。

此外,如果我的顶级导航开始换行,当您单击第 2 行的 link 之一时,子菜单将显示在顶级 link,几乎无法关闭。有没有办法解决这个问题?

这是我的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 包含在 jsFiddle 中

您查找 $(this).offset(); 它告诉您元素相对于文档的位置,因此您需要减去导航栏的位置以获得元素的顶部位置。因为你想在被点击的元素下方显示菜单,你还必须将被点击元素的高度添加到 TOP 位置以将其推到下方。请参阅此代码段以了解工作代码和注释代码

编辑:这是作为答案发布的部分问题(由 OP 在评论中提出)的答案,因此我可以添加可运行的代码段。还折叠了代码片段,因此它不会占用太多 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>

哈!我能够自己找出答案!

我在这里定义了变量:

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

我所要做的就是将 activeTab 变量更改为:

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

完美运行!现在这段代码适用于所有菜单,我不必再重复了!