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

前端 未结 3 431
栀梦
栀梦 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;
    }
    <p class="test">Testing</p>
    <div>No classes here</div>
    <ul>
      <li class="test">Something in a 'test' element</li>
    </ul>

    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;
    }
    <p class="test">Testing</p>
    <div>No classes here</div>
    <ul>
      <li class="test">Something in a 'test' element</li>
    </ul>

    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;
    }
    <p class="test">Testing</p>
    <div>No classes here</div>
    <ul>
      <li class="test">Something in a 'test' element</li>
    </ul>

    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;
    }
    <p class="test">Testing</p>
    <div>No classes here</div>
    <ul>
      <li class="test">Something in a 'test' element</li>
    </ul>

    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().
    0 讨论(0)
  • 2021-01-21 14:08

    Here's a working example (which requires JQuery). When a member of p1 is moused over, all other elements of p1 will be highlighted as well. The same is true of p2.

    JavaScript:

    function highlightAllOnMouseover(className){
        $(className).mouseover(function() {
      $(className).css("opacity", 0.4); 
      $(className).css("opacity", 1);
    }).mouseleave(function() { 
        $(className).css("opacity", 0.4);
    });
    }
    highlightAllOnMouseover(".thing1");
    highlightAllOnMouseover(".thing2");
    

    HTML:

    <p class = "thing1">This is thing1.</p>
    <p class = "thing2">This is thing2.</p>
    <p class = "thing1">This is also thing1.</p>
    <p class = "thing2">This is also thing2.</p>
    

    To cause all elements with a specific class to be highlighted on mouseover, you only need to call the function highlightAllOnMouseover(className), which I created here.

    0 讨论(0)
  • 2021-01-21 14:12

    Here is a zero-dependency solution that should work with very old JS versions:

    1. Add class = 'grp_N hovergrp' to all elements that should be highlighted on hover, replacing N by some number (or id) that uniquely describes a group of elements. The groups may not intersect, every element with hovergrp class should belong to exactly one grp_N class.

    2. Append the following JS snippet in a <script>...</script> to the end of your <html>:

      // collect all highlighted elements and group them by their group 
      // name for faster access;
      // Attach onmouseover and onmouseout listeners.
      var groups = {};
      var hovergrp = document.getElementsByClassName("hovergrp"); 
      for (var i = 0; i < hovergrp.length; i++) {
        var e = hovergrp.item(i);
        var eClasses = e.classList;
        for (var j = 0; j < eClasses.length; j++) {
          var c = eClasses[j];
          if (c.startsWith("grp_")) {
            if (!groups[c]) {
              groups[c] = [];
            }
            groups[c].push(e);
            e.onmouseover = (function(c_capture) {
              return function(_event) {
                highlightGroup(c_capture, "orange");
              };
            })(c);
            e.onmouseout = (function(c_capture) {
              return function(_event) {
                highlightGroup(c_capture, "transparent");
              };
            })(c);
            break;
          }
        }
      }
      
      function highlightGroup(groupName, color) {
        var g = groups[groupName];
        for (var i = 0; i < g.length; i++) {
          g[i].style.backgroundColor = color;
        }
      }
      <pre><code>
        // hover over variable names `<span class='grp_0 hovergrp'>x</span>` and `<span class='grp_1 hovergrp'>f</span>`
        kroneckerDelta(<span class='grp_0 hovergrp'>x</span>) {
          return function(<span class='grp_1 hovergrp'>f</span>) {
            <span class='grp_1 hovergrp'>f</span>(<span class='grp_0 hovergrp'>x</span>)
          }
        }
      </code></pre>
    
    <p class = "grp_p1 hovergrp">This should be highlighted on mouseover</p>
    <p class = "grp_p2 hovergrp">This should be highlighted on mouseover</p>
    <p class = "grp_p1 hovergrp">This should be highlighted on mouseover</p>
    <p class = "grp_p2 hovergrp">This should be highlighted on mouseover</p>

    The HTML snippet shows usage example: a little <pre>-formatted snippet of code with variables that are grouped into two groups. Whenever you hover over a variable, all the usages of the variable as well as the binding site are highlighted.

    0 讨论(0)
提交回复
热议问题