如何避免多次穿越相同的 DOM 路线?

How can I avoid traversing the same DOM route multiple times?

我有一个 on 函数,其中有两个鼠标事件 mouseentermouseleave。当这些事件被触发时,它们 运行 不同的功能,一个添加 class,另一个删除它。

$(this).siblings('.testimonial').find('p').addClass('unseen');

$(this).siblings('.testimonial').find('p').removeClass('unseen');

问题是,我正在执行以下 DOM 遍历两次:

$(this).siblings('.testimonial').find('p')

但我不知道如何将此遍历保存为一个函数中的变量并将其用作另一个函数。这是我的完整 JS 代码:

JavaScript

(function ($) {

    var testimonial = $('.testimonial');
    var testimonialHeight = testimonial.outerHeight();
    var testimonialWidth = testimonial.outerWidth();

    testimonial.find('p').addClass('unseen');

    testimonial.css({
        height: testimonialHeight,
        width: testimonialWidth
    });

    $('.client').on({
        mouseenter: function() {
            $(this).siblings('.testimonial').find('p').removeClass('unseen');
        },
        mouseleave: function() {
            $(this).siblings('.testimonial').find('p').addClass('unseen');
        }
    });

})(jQuery);

HTML

<ul class="list-unstyled list-inline">
  <li>
    <div class="testimonial"><p>Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged.<p></div>
      <img class="client" src="https://s3.amazonaws.com/uifaces/faces/twitter/jsa/128.jpg" alt="" />
  </li>
  <li>
    <div class="testimonial"><p>Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged.</p></div>
    <img class="client" src="https://s3.amazonaws.com/uifaces/faces/twitter/gerrenlamson/128.jpg" alt="" />
  </li>
  <li>
    <div class="testimonial"><p>Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book.</p></div>
    <img class="client" src="https://s3.amazonaws.com/uifaces/faces/twitter/jadlimcaco/128.jpg" alt="" />
  </li>
</ul>

有人可以建议更好的方法吗?

谢谢。

您可以简单地执行以下操作:

var $client = $('.client');
var $clientTestimonialParagraphs = $client.siblings('.testimonial').find('p');

$client.on({
        mouseenter: function() {
            $clientTestimonialParagraphs.removeClass('unseen');
        },
        mouseleave: function() {
            $clientTestimonialParagraphs.addClass('unseen');
        }
    });

希望对您有所帮助。

尝试存储所选项目

var p = testimonial.find('p').addClass('unseen');

然后对存储的进行运算

$('.clienet').hover(
    function(){ p.removeClass('unseen') }, 
    function(){ p.addClass('unseen') }
)

如果确实需要缓存特定的兄弟姐妹子元素而不是所有子元素,您可以使用以下逻辑.testimonial p:

$('.client').each(function () {
    this._testimonialP = $(this).siblings('.testimonial').find('p').addClass('unseen');// But .unseen should be set in HTML markup by default
}).hover(function () {
    this._testimonialP.toggleClass('unseen');
});

如果您正在寻找一种 DRY 方法来做到这一点,您可以编写一个可重用的函数来找到您想要的兄弟姐妹。

function findTestimonialParagraph($root) {
    return $root.siblings('.testimonial').find('p');
}

$('.client').on({
    mouseenter: function() {
        findTestimonialParagraph($(this)).removeClass('unseen');
    },
    mouseleave: function() {
        findTestimonialParagraph($(this)).addClass('unseen');
    }
});

这样,如果您需要更改评价段落的访问方式,只需在一个地方进行。

您可以更改为对两个事件使用一个公共事件处理程序,并根据它是哪个事件来设置操作:

$('.client').on("mouseenter mouseleave", function(e) {
    var method = e.type === "mouseenter" ? "removeClass" : "addClass";
    $(this).siblings('.testimonial').find('p')[method]('unseen');
});

下面是对正在发生的事情的解释:

  • .on("mouseenter mouseleave", function(e) {...}) 将多个事件连接到同一个事件处理程序。
  • e.type 是当前事件的事件名称,因此当您有多个事件触发同一个事件处理程序时,您可以看到触发的是哪个事件。
  • var method = e.type === "mouseenter" ? "removeClass" : "addClass" 类似于 if/else 语句,它根据 [=15] 的值将 "removeClass""addClass" 分配给 method 变量=].它被称为 ternary operator.
  • obj[method] 是一个 属性 引用,使用变量作为 属性 的名称而不是字符串文字。所以当 method"addClass"obj.addClassobj[method] 相同。在末尾添加 () 使其成为函数调用,然后 obj.addClass('unseen')obj[method]('unseen') 相同,当 method"addClass".

因此,再次打破最后一行:

// find the right paragraphs
$(this).siblings('.testimonial').find('p')

// get the property whose name is in the method variable
$(this).siblings('.testimonial').find('p')[method]

// call that property as a function and pass it 'unseen'
$(this).siblings('.testimonial').find('p')[method]('unseen');

一个可能对 DRY 有用的工具是 .hover(),因为它是 mouseenter 和 mouseleave 的快捷方式。如果您知道相关段落在悬停之前总是被标记为看不见,并且页面中没有其他代码与看不见的 class 混淆(您在问题中没有说什么),那么您可以使用使用 .hover()

的快捷方式
$('.client').hover(function() {
    $(this).siblings('.testimonial').find('p').toggleClass('unseen');
});

将重复代码移动到可在两个地方使用的通用函数的更常见方法如下所示:

function getSiblingParagraphs(parent) {
    return $(parent).siblings('.testimonial').find('p');
}

$('.client').on({
    mouseenter: function() {
        getSiblingParagraphs(this).removeClass('unseen');
    },
    mouseleave: function() {
        getSiblingParagraphs(this).addClass('unseen');
    }
 });