案例研究:jQuery 修复升级

Avatar of Chris Coyier
Chris Coyier

DigitalOcean 为您旅程的每个阶段提供云产品。立即开始使用 200美元免费额度!

最近在 CSS-Tricks 论坛,名为 Waffle 的用户 发布了以下 jQuery JavaScript 代码片段

$(function() {

  $('.ContactArea').hide();
                
  $('.Portfolio').hide();
                
  $('.WebDesign').hide();

  $('.AboutCoadin').hide();

  $('li.Contact').click(function(){
    $(".ContactArea").slideToggle();
  });

  $('li.PortfolioBtn').click(function(){
    $(".Portfolio").slideToggle();
  });

  $('li.WebDesignBtn').click(function(){
    $(".WebDesign").slideToggle();
  });

  $('li.AboutBtn').click(function(){
    $(".AboutCoadin").slideToggle();
  });
                        
});

这位用户的实际问题是如何扩展他们的代码,以便在区域展开时向下滚动页面。但是,用稍微更有经验的眼光来看待这样的代码,有一些东西会脱颖而出,成为我们可以改进的地方。因为我敢打赌,你们中的一些人处于 Waffle 的水平,并编写了这样的代码,所以我认为我们可以用它作为一个案例研究来学习。

它有效,但是…

首先,Waffle 的代码没有 **损坏**,它运行良好,并且由于其简单的特性,我们最终做出的改进可能甚至不会带来明显的加速。但是,我们做出的改进应该使代码更易读、更高效、更具扩展性,并改进其他相关内容。此外,了解我们做出的改进将使我们将来在利害关系更高的场合成为更好的程序员。

我们需要 DOM 就绪语句吗?

第一行代码是

$(function() {

…这是 jQuery 的简写,意思是“当 DOM 准备好被操作时,运行此函数…” 这是一个安全网,例如,选择器不会在尚未解析的 HTML 上运行,让我们想知道 WTF。它也不会等到页面完全加载,这会使我们的脚本看起来很迟钝。

但问题是,我们应该在页面底部(在结束的 </body> 标签之前)运行这样的脚本,以便加载脚本不会阻碍页面渲染。如果我们在页面底部正确加载脚本,则不需要 DOM 就绪语句,因为浏览器在开始读取脚本之前就已经处理了所有这些 HTML。

多个选择器,一个 jQuery 对象

接下来的几行代码是

$('.ContactArea').hide();             
$('.Portfolio').hide();     
$('.WebDesign').hide();
$('.AboutCoadin').hide();

在这里创建四个单独的 jQuery 对象是不必要的,因为您可以在一个对象中使用多个选择器。这可以做到同样的事情

$(".ContactArea, .Portfolio, .WebDesign, .AboutCoadin").hide();

就像 CSS 选择器一样。但是,您可能确实需要四个单独的 jQuery 对象,以便您以后无需重新创建它们即可引用它们。在这种情况下,设置变量

var contactArea = $('.ContactArea').hide(),
    portfolio   = $('.Portfolio').hide(),
    webDesign   = $('.WebDesign').hide(),
    about       = $('.AboutCoadin').hide();

您还可以考虑向 HTML 添加一个类名,例如“initial-hide”,并将其应用于您希望在页面加载时隐藏的所有元素,并通过该类名进行选择,但这可能会违反语义。

ID

很难说,除非看到真实的网站,但我感觉这些区域,如“联系区域”和“作品集”,在页面上是完全唯一的区域,而不是重复出现多次的区域。完全唯一的区域非常适合使用 ID(而不是类)。这里的优势在于 ID 选择器速度更快。

$("#ContactArea"); // is faster than
$(".ContactArea");

关于 ID 的另一个重要事项是它们是自然的跳转链接,无论是否启用 JavaScript 都可以工作

<!-- This link will jump down the page ... -->
<a href="#ContactArea">Contact</a>

<!-- ... to ensure this element is visible -->
<section id="ContactArea"></section>

您可能实际上不希望页面向下跳转,但您可以通过 阻止默认行为 来使用 JavaScript 停止它。如果没有 JavaScript,您绝对希望同页导航是跳转链接。

选择合适的元素

接下来的代码片段有四个重复的部分

$('li.Contact').click(function(){
  $(".ContactArea").slideToggle();
});

$('li.PortfolioBtn').click(function(){
  $(".Portfolio").slideToggle();
});

$('li.WebDesignBtn').click(function(){
  $(".WebDesign").slideToggle();
});

$('li.AboutBtn').click(function(){
  $(".AboutCoadin").slideToggle();
});

首先要讨论的是实际被选择的元素。大概,标记如下所示

<nav>
  <ul>
     <li class="ContactArea"><a href="#">Contact</a></li>
     <li class="PortfolioBtn"><a href="#">Contact</a></li>
     <li class="WebDesignBtn"><a href="#">Contact</a></li>
     <li class="AboutBtn"><a href="#">Contact</a></li>
  </ul>
</nav>

jQuery 代码通过它们的类名选择列表项。这些是我的问题

  1. 它们并非都使用“Btn”约定。如果您坚持使用这样的类,您最好使其标准化。在如此狭小的范围内不坚持使用它将是其他更大规模的不一致的症状。
  2. 您不需要类来唯一地设置它们的样式。您可以使用 :nth-child 或 :nth-of-type。除非您需要支持非常旧的浏览器,在这种情况下,无论如何您都在使用 jQuery,并且可以使用它来伪造支持。
  3. 因为您不需要它们来设置样式,所以我想说您实际上根本不需要它们

假设标记可以更改为使用 ID 而不是类,则可以将 HTML 重写如下

<nav>
  <ul>
     <li><a href="#ContactArea">Contact</a></li>
     <li><a href="#Portfolio">Portfolio</a></li>
     <li><a href="#WebDesign">Web Design</a></li>
     <li><a href="#AboutCoadin">About</a></li>
  </ul>
</nav>

这是一种更具语义的导航表示。没有不必要的类和作为指向相应内容的同页链接的锚链接。

不要重复自己

通常称为“DRY”。执行所有这些选择和 slideToggling 的四个语句非常重复。我们可以使用我们新的改进的 HTML 结构来改进它。

导航中这些锚链接的 href 属性类似于“#Contact”,这正是我们作为选择器字符串查找该元素所需的。非常方便! 让我们从链接中提取该 href 并将其用作选择器,这样,一个单一的点击处理程序就可以处理我们的所有四个链接

$("nav a").click(function(e){
  e.preventDefault();
  $($(this).attr("href")).slideToggle();
});

最终代码

我们的最终代码变得更加简洁

$("#ContactArea, #WebDesign, #Portfolio, #AboutCoadin").hide();

$("nav a").click(function(e){
  e.preventDefault();
  $($(this).attr("href")).slideToggle();
});

查看演示

实际回答 Waffle 的问题

提出的问题实际上是“我还希望在按下这些按钮时页面滚动到这些 div” 我想说,你确定吗?这似乎是动画活动的奇怪组合。平滑滚动同页链接实际上比您想象的要复杂一些,但我有一些很棒的 可复制粘贴的代码。问题在于它在这种情况下不太适用,因为在单击链接时,要滚动到的元素实际上是隐藏的。只有在 slideToggle 完成后,元素才会显示并达到其最终大小。您必须延迟页面滚动,直到 回调函数 为止。可以做到,但很笨拙。

我做得好吗?

我最喜欢的在这个网站上发布博文的部分之一是,人们真的会花时间阅读它并提供高质量的建议。如果您有一些更好的方法来处理我上面提到的内容,我很乐意听取您的意见!