首页 > 解决方案 > jQuery Find Closest Element Inside div Based on mousemove Event

问题描述

Google uses a feedback feature that highlights the background color of content elements (ex:p, div, ul, h2, etc.) when the user mouses over a div to the right side of the content.

I believe the following CSS class is applied to the element to highlight its background:

.inline-feedback__highlight {
    background: #d2e3fc;
    -webkit-border-radius: .3125rem;
    border-radius: .3125rem;
}

Using jQuery or JavaScript and CSS, I'd like to achieve the same result.

My Question

How can I identify what the closest element in <div id="content">...</div> is?

I was thinking some form of x,y coordinates and offset from the top of the content div.

My Code

$(function() {
  let halfBtnHt = Math.ceil($('#track-button-div').height() / 2);

  $('#track-container').on('mousemove', function(e) {
    // console.log(e.offsetX, e.offsetY);
    $('#track-button').css({
      'transform': `translateX(0) translateY(${e.offsetY - halfBtnHt}px)`,
      'visibility': 'visible',
    })

  }).on('mouseout', function(e) {
    $('#track-button').css({
      'visibility': 'hidden'
    })
  })

})
#content-container {
  position: relative;
  border: 1px solid black;
  width: 500px;
  height: auto;
  margin: 100px auto;
}

#content {
  padding: 2rem;
}

#track-container {
  position: absolute;
  text-align: center;
  top: 0;
  bottom: 0;
  width: 64px;
  right: -56px;
  z-index: 1;
}

#track-button {
  width: 42px;
  height: 42px;
  border-radius: 30px;
  pointer-events: none !important;
}

#track-button-div {
  visibility: hidden;
}
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <title>Title</title>
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.5.3/dist/css/bootstrap.min.css" integrity="sha384-TX8t27EcRE3e/ihU7zmQxVncDAy5uIKz4rEkgIXeMed4M0jlfIDPvg6uqKI2xXr2" crossorigin="anonymous">
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.1/css/all.min.css" integrity="sha512-+4zCK9k+qNFUR5X+cKL9EIR+ZOhtIloNl9GIKS57V1MyNsYpYcUrUeQc9vNfzsWfV28IaLL3i96P9sdNyeRssA==" crossorigin="anonymous" />
  <link rel="stylesheet" href="style.css">
</head>

<body>
  <div id="content-container">
    <div id="content">
      <div>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Aliquam, iusto? Lorem ipsum dolor sit amet, consectetur adipisicing elit. Consequuntur dolores earum esse eveniet libero minima pariatur repellat sed sunt ut?</div>
      <pre class="prettyprint linenums prettyprinted">
                    <ol class="linenums">
                        <li class="L0">Hey</li>
                    </ol>
                </pre>
      <p>Blanditiis corporis ducimus laudantium nisi pariatur quasi repellat sunt, ut? Consequuntur dolores earum</p>
    </div>
    <div id="track-container">
      <div id="track-button-div">
        <button id="track-button" class="btn btn-outline-primary">
                        <i class="fas fa-quote-right"></i>
                    </button>
      </div>
    </div>
  </div>
  <script src="https://code.jquery.com/jquery-3.5.1.slim.min.js" integrity="sha384-DfXdz2htPH0lsSSs5nCTpuj/zy4C+OGpamoFVy38MVBnE+IbbVYUew+OrCXaRkfj" crossorigin="anonymous"></script>
  <script src="https://cdn.jsdelivr.net/npm/bootstrap@4.5.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-ho+j7jyWK8fNQe+A12Hb8AhRq26LrZ/JpcUGGOn+Y7RsweNrtN/tE3MoK7ZeZDyx" crossorigin="anonymous"></script>
  <script src="track.js"></script>
</body>

</html>


Here's what Google's Feedback Feature Looks Like

inline-track

标签: javascriptjquerycss

解决方案


看下面的片段:

(function ($) {
    'use strict';

    $(function () {
        var
            namespace = 'mmdm',
            //-----
            mainElementID = '#__elements_container',
            highlightClass = 'founded-element__highlight',
            //-----
            mainElement = $(mainElementID),
            movableElementContainer = $('#__movable_element_container'),
            movableElement = $('#__movable_element');

        // some utility

        function getTouch(event) {
            var touch = event;
            if (('ontouchstart' in document.documentElement) || navigator.maxTouchPoints > 0) {
                touch = event.originalEvent.touches && event.originalEvent.touches.length ? event.originalEvent.touches[0] : event;
                if (event.type === 'touchstart' || event.type === 'touchmove') {
                    touch = event.targetTouches[0] || event.changedTouches[0];
                }
            }
            return touch;
        }

        // define function(s)
        function removeHighlightClass() {
            mainElement.find('*').removeClass(highlightClass);
        }

        function findElementsWithSameYNHighlightIt(e) {
            var x, y, meOffset, el;
            meOffset = mainElement.offset();
            x = (e.pageX - meOffset.left) / 2;
            y = e.pageY - $(window).scrollTop();

            el = document.elementFromPoint(x, y);
            if (!$(el).is(mainElement) && $(el).closest(mainElementID).length) {
                $(el).addClass(highlightClass);
            }
        }

        function showMovableElement() {
            movableElement.addClass('show');
        }

        function hideMovableElement() {
            movableElement.removeClass('show');
        }

        function moveMovableElement(e) {
            var y, mecTop = movableElementContainer.offset().top;
            y = e.pageY;
            // bound move to the main movable container
            if (y >= mecTop && y <= (mecTop + movableElementContainer.outerHeight())) {
                movableElement.css({
                    'top': y - mecTop - (movableElement.outerHeight() / 2)
                });
            }
            removeHighlightClass();
        }

        // attach event(s)
        movableElementContainer
            .on('mousemove.' + namespace + ' touchmove.' + namespace + ' mouseenter.' + namespace + ' touchstart.' + namespace, function (e) {
                if (!e.defaultPrevented && e.cancelable) {
                    e.preventDefault();
                }
                //-----
                var touch = getTouch(e);
                showMovableElement();
                moveMovableElement(touch);
                findElementsWithSameYNHighlightIt(touch);
            }).on('mouseleave.' + namespace + ' touchend.' + namespace, function (e) {
            if (!e.defaultPrevented && e.cancelable) {
                e.preventDefault();
            }
            //-----
            hideMovableElement();
            removeHighlightClass();
        });
    });
})(jQuery);
* {
  box-sizing: border-box;
}

#__elements_main_container {
  display: flex;
}

#__elements_container {
  width: 500px;
}

#__movable_element_container {
  position: relative;
  width: 40px;
}

#__movable_element_container::after {
  content: '';
  position: absolute;
  top: 0;
  left: 50%;
  width: 1px;
  height: 100%;
  background-color: #ccc;
  transform: translate(-50%);
  z-index: 1;
}

#__movable_element  {
  position: absolute;
  display: none;
  align-items: center;
  justify-content: center;
  left: 50%;
  width: 40px;
  height: 40px;
  text-align: center;
  border-radius: 50rem;
  border: 1px solid #ccc;
  background-color: #fff;
  box-shadow: 0 2px 5px rgba(0, 0, 0, .26);
  transform: translate(-50%);
  z-index: 2;
}

#__movable_element.show {
  display: flex;
}

.founded-element__highlight {
    background-color: #cecdff;
    border-radius: 3px;
}
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.css" rel="stylesheet"/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>

<div id="__elements_main_container">
  <div id="__elements_container">
    <h1>
      A heading tag!
    </h1>
    <p>
      Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
    </p>
    <ul>
      <li>First list item</li>
      <li>Second list item</li>
    </ul>
  </div>
  <div id="__movable_element_container">
    <i id="__movable_element" class="fa fa-quote-right"></i>
  </div>
</div>


推荐阅读