Google использует функцию обратной связи , которая выделяет цвет фона элементов контента (например: p
, div
, ul
, h2
и т. д.), когда пользователь наводит указатель мыши на элемент div с правой стороны контента.
Я считаю, что следующий класс CSS применяется к элементу, чтобы выделить его фон:
.inline-feedback__highlight {
background: #d2e3fc;
-webkit-border-radius: .3125rem;
border-radius: .3125rem;
}
Используя jQuery или JavaScript и CSS, я хотел бы добиться того же результата.
Мой вопрос
Как я могу определить, какой ближайший элемент в <div id = "content">...</div>
?
Я думал о какой-то форме координат x, y и смещении от верхней части содержимого div.
Мой код
$(function() {
let halfBtnHt = Math.ceil($('#track-button-div').height() / 2);
$('#track-container').on('mousemove', function(e) {
// console.info(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/[email protected]/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/[email protected]/dist/js/bootstrap.bundle.min.js" integrity = "sha384-ho+j7jyWK8fNQe+A12Hb8AhRq26LrZ/JpcUGGOn+Y7RsweNrtN/tE3MoK7ZeZDyx" crossorigin = "anonymous"></script>
<script src = "track.js"></script>
</body>
</html>
Вот как выглядит функция обратной связи Google
Пожалуйста, не судите о качестве кода, я сделал его только для тестирования и заставил его делать то, что вы изначально просили о выборе ближайшего элемента.
Проверьте эту песочницу на рабочем примере
Ключевым моментом здесь является использование функции elementFromPoint, песочница должна просто давать общее представление, и вы можете адаптировать ее к своим потребностям!
Не беспокойтесь, рад помочь.
Посмотрите на фрагмент ниже:
(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>
+1 Отличный ответ. Я выбрал ответ MMDM, потому что он дал мне именно то, что мне было нужно. Я продолжал натыкаться на
document.elementFromPoint(x,y)
, но не мог придумать, как это сделать. Ваше решение очень полезно!