Highlight all elements with same class when one of them is moused over

前端 未结 3 436
栀梦
栀梦 2021-01-21 13:40

In JavaScript, is it possible to highlight all items with the same class when one of them is moused over?

For example, if I had two paragraphs with the class p1

3条回答
  •  攒了一身酷
    2021-01-21 14:04

    I can't help but feel this should be more concise (the use of three for (...) loops feels unnecessarily expensive), but one approach:

    Object.prototype.classHighlight = function (over, out) {
        var that = this.length ? this : [this];
        function onOver() {
            for (var i = 0, len = that.length; i < len; i++) {
                that[i].style.backgroundColor = over;
            }
        }
        function onOut() {
            for (var i = 0, len = that.length; i < len; i++) {
                that[i].style.backgroundColor = out;
            }
        }
        for (var i = 0, len = that.length; i < len; i++) {
            that[i].onmouseover = onOver;
            that[i].onmouseout = onOut;
        }
    };
    
    document.getElementsByClassName('test').classHighlight('#f90', '#fff');
    

    JS Fiddle demo.

    Six years later, following a link to this question and answer, I'm editing to update the above approach, and to add snippets and references.

    Updated code:

    // extending the Object prototype to allow chaining of this method,
    // 'over' : String, the class-name to add when the element(s) of the
    // HTMLCollection/NodeList are hovered-over. We also set the default
    // value of the 'over' variable in order that a class-name will always
    // be present:
    Object.prototype.classHighlight = function(over = 'over') {
    
      // taking the 'this' and using the spread operator to expand
      // the iterable collection to an Array:
      const that = [...this],
    
        // creating a named function to act as the event-handler for
        // 'mouseenter' and 'mouseleave':
        toggleHighlight = (event) => {
          // iterating over the array using Array.prototype.forEach():
          that.forEach(
    
            // we're not using 'this' in here, so using an Arrow function
            // to use the Element.classList API to toggle the supplied
            // class on each element of the collection. If the event-type
            // is exactly equal to 'mouseenter' we add the class otherwise
            // we remove the class:
            (el) => el.classList.toggle(over, event.type === 'mouseenter')
          );
        };
      // iterating over the collection, again using Array.prototype.forEach():
      that.forEach(
        // and another Arrow function:
        (element) => {
    
          // here we bind the toggleHighlight function - created above - as
          // the event-handler for both the 'mouseenter' and 'mouseleave'
          // events:
          element.addEventListener('mouseenter', toggleHighlight);
          element.addEventListener('mouseleave', toggleHighlight);
        });
    };
    
    // here we use document.getElementsByClassName() to retrieve an HTMLCollection
    // of elements matching the supplied class-name; and then using chaining - which
    // is why we extended the Object prototype - to pass that HTMLCollection to
    // the classHighlight() function:
    document.getElementsByClassName('test').classHighlight('whenOver');
    .whenOver {
      background-color: #f90;
    }

    Testing

    No classes here
    • Something in a 'test' element

    Note that this updated approach, because we're toggling a class-name – as opposed to adding and clearing inline styles in the elements' style attribute – means that selector-specificity may interfere with application of the style, for example:

    // extending the Object prototype to allow chaining of this method,
    // 'over' : String, the class-name to add when the element(s) of the
    // HTMLCollection/NodeList are hovered-over. We also set the default
    // value of the 'over' variable in order that a class-name will always
    // be present:
    Object.prototype.classHighlight = function(over = 'over') {
    
      // taking the 'this' and using the spread operator to expand
      // the iterable collection to an Array:
      const that = [...this],
    
        // creating a named function to act as the event-handler for
        // 'mouseenter' and 'mouseleave':
        toggleHighlight = (event) => {
          // iterating over the array using Array.prototype.forEach():
          that.forEach(
    
            // we're not using 'this' in here, so using an Arrow function
            // to use the Element.classList API to toggle the supplied
            // class on each element of the collection. If the event-type
            // is exactly equal to 'mouseenter' we add the class otherwise
            // we remove the class:
            (el) => el.classList.toggle(over, event.type === 'mouseenter')
          );
        };
      // iterating over the collection, again using Array.prototype.forEach():
      that.forEach(
        // and another Arrow function:
        (element) => {
    
          // here we bind the toggleHighlight function - created above - as
          // the event-handler for both the 'mouseenter' and 'mouseleave'
          // events:
          element.addEventListener('mouseenter', toggleHighlight);
          element.addEventListener('mouseleave', toggleHighlight);
        });
    };
    
    // here we use document.getElementsByClassName() to retrieve an HTMLCollection
    // of elements matching the supplied class-name; and then using chaining - which
    // is why we extended the Object prototype - to pass that HTMLCollection to
    // the classHighlight() function:
    document.getElementsByClassName('test').classHighlight('whenOver');
    li.test {
      background-color: fuchsia;
    }
    .whenOver {
      background-color: #f90;
    }

    Testing

    No classes here
    • Something in a 'test' element

    This can be resolved by increasing the selector specificity of the assigned class-name:

    li.test {
      background-color: fuchsia;
    }
    
    html body .whenOver {
      background-color: #f90;
    }
    

    Object.prototype.classHighlight = function(over = 'over') {
    
      const that = [...this],
    
        toggleHighlight = (event) => {
          that.forEach(
            (el) => el.classList.toggle(over, event.type === 'mouseenter')
          );
        };
      that.forEach(
        (element) => {
          element.addEventListener('mouseenter', toggleHighlight);
          element.addEventListener('mouseleave', toggleHighlight);
        });
    };
    
    document.getElementsByClassName('test').classHighlight('whenOver');
    li.test {
      background-color: fuchsia;
    }
    
    html body .whenOver {
      background-color: #f90;
    }

    Testing

    No classes here
    • Something in a 'test' element

    Or, you could instead use the !important keyword to force that !important-ified property to apply regardless of specificity (unless another rule also uses !important and is itself more specific), for example:

    /* Note the ridiculous and overly-specific selector: */
    html > body > ul > li.test {
      background-color: fuchsia;
    }
    
    .whenOver {
      / and here, as the demo shows, !important still
        wins: */
      background-color: #f90 !important;
    }
    

    Object.prototype.classHighlight = function(over = 'over') {
    
      const that = [...this],
    
        toggleHighlight = (event) => {
          that.forEach(
            (el) => el.classList.toggle(over, event.type === 'mouseenter')
          );
        };
      that.forEach(
        (element) => {
          element.addEventListener('mouseenter', toggleHighlight);
          element.addEventListener('mouseleave', toggleHighlight);
        });
    };
    
    document.getElementsByClassName('test').classHighlight('whenOver');
    html > body > ul > li.test {
      background-color: fuchsia;
    }
    
    .whenOver {
      background-color: #f90 !important;
    }

    Testing

    No classes here
    • Something in a 'test' element

    When it comes to !important, though, try to avoid using it wherever possible because, as MDN notes:

    Using !important, however, is bad practice and should be avoided because it makes debugging more difficult by breaking the natural [cascade] in your stylesheets.

    "The !important exception," MDN.

    References:

    • CSS:
      • Selector specificity. *JavaScript:
      • Array.prototype.forEach().
      • Arrow functions.
      • document.getElementsByClassName().
      • Element.classList.
      • EventTarget.addEventListener().

提交回复
热议问题