Skip to content

Moving the mouse #620

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 9 commits into from
Mar 18, 2024
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
<style>
body {
height: 2000px;
/* the tooltip should work after page scroll too */
/* спливаюча підказка також має працювати після прокручування сторінки */
}

.tooltip {
Expand Down Expand Up @@ -49,42 +49,42 @@
<body>


<div data-tooltip="Here is the house interior" id="house">
<div data-tooltip="Here is the roof" id="roof"></div>
<div data-tooltip="Ось – інтер’єр будинку" id="house">
<div data-tooltip="Ось – дах" id="roof"></div>

<p>Once upon a time there was a mother pig who had three little pigs.</p>
<p>Жила-була мама-свиня, у якої було троє поросят.</p>

<p>The three little pigs grew so big that their mother said to them, "You are too big to live here any longer. You must go and build houses for yourselves. But take care that the wolf does not catch you."</p>
<p>Троє поросят виросли такі великі, що їхня мати сказала їм: "Ви занадто великі, щоб жити тут далі. Ви повинні йти і будувати собі будинки. Але бережіть себе, щоб вовк вас не спіймав."</p>

<p>The three little pigs set off. "We will take care that the wolf does not catch us," they said.</p>
<p>Троє поросят рушили. "Будемо берегти один одного, щоб вовк нас не спіймав", - сказали вони.</p>

<p>Soon they met a man. <a href="https://en.wikipedia.org/wiki/The_Three_Little_Pigs" data-tooltip="Read on…">Hover over me</a></p>
<p>Незабаром вони зустріли чоловіка.<a href="https://uk.wikipedia.org/wiki/Троє_поросят" data-tooltip="Читайте далі…">Наведіть на мене мишу</a></p>

</div>

<script>
let tooltip;

document.onmouseover = function(event) {
// important: a fast-moving mouse may "jump" right to a child on an annotated node, skipping the parent
// so mouseover may happen on a child.
// важливо: миша, що швидко рухається, може "стрибнути" прямо до дочірнього вузла, пропускаючи батьківський
// тому mouseover може статися на дочірньому елементі.

let anchorElem = event.target.closest('[data-tooltip]');

if (!anchorElem) return;

// show tooltip and remember it
// показати підказку та запам'ятати її
tooltip = showTooltip(anchorElem, anchorElem.dataset.tooltip);
}

document.onmouseout = function() {
// it is possible that mouseout triggered, but we're still inside the element
// (its target was inside, and it bubbled)
// but in this case we'll have an immediate mouseover,
// so the tooltip will be destroyed and shown again
// можливо, спрацював mouseout, але ми все ще всередині елемента
// (target цієї події всередині, і подія спливає)
// але в цьому випадку ми маємо негайне спрацювання події mouseover,
// тому спливаючу підказку буде знищено та показано знову
//
// luckily, the "blinking" won't be visible,
// as both events happen almost at the same time
// на щастя, "миготіння" не буде видно,
// оскільки обидві події відбуваються майже одночасно
if (tooltip) {
tooltip.remove();
tooltip = false;
Expand All @@ -101,7 +101,7 @@

let coords = anchorElem.getBoundingClientRect();

// position the tooltip over the center of the element
// розмістити підказку над центром елемента
let left = coords.left + (anchorElem.offsetWidth - tooltipElem.offsetWidth) / 2;
if (left < 0) left = 0;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
<style>
body {
height: 2000px;
/* the tooltip should work after page scroll too */
/* спливаюча підказка також має працювати після прокручування сторінки */
}

.tooltip {
Expand Down Expand Up @@ -49,21 +49,21 @@
<body>


<div data-tooltip="Here is the house interior" id="house">
<div data-tooltip="Here is the roof" id="roof"></div>
<div data-tooltip="Ось – інтер’єр будинку" id="house">
<div data-tooltip="Ось – дах" id="roof"></div>

<p>Once upon a time there was a mother pig who had three little pigs.</p>
<p>Жила-була мама-свиня, у якої було троє поросят.</p>

<p>The three little pigs grew so big that their mother said to them, "You are too big to live here any longer. You must go and build houses for yourselves. But take care that the wolf does not catch you."</p>
<p>Троє поросят виросли такі великі, що їхня мати сказала їм: "Ви занадто великі, щоб жити тут далі. Ви повинні йти і будувати собі будинки. Але бережіть себе, щоб вовк вас не спіймав".</p>

<p>The three little pigs set off. "We will take care that the wolf does not catch us," they said.</p>
<p>Троє поросят рушили. "Будемо берегти один одного, щоб вовк нас не спіймав", - сказали вони.</p>

<p>Soon they met a man. <a href="https://en.wikipedia.org/wiki/The_Three_Little_Pigs" data-tooltip="Read on…">Hover over me</a></p>
<p>Незабаром вони зустріли чоловіка.<a href="https://uk.wikipedia.org/wiki/Троє_поросят" data-tooltip="Читайте далі…">Наведіть на мене вказівник</a></p>

</div>

<script>
// ...your code...
// ...ваш код...
</script>

</body>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,24 +2,24 @@ importance: 5

---

# Improved tooltip behavior
# Покращена поведінка спливаючої підказки

Write JavaScript that shows a tooltip over an element with the attribute `data-tooltip`. The value of this attribute should become the tooltip text.
Напишіть JavaScript, який покаже спливаючу підказку над елементом із атрибутом `data-tooltip`. Значення цього атрибута має стати текстом підказки.

That's like the task <info:task/behavior-tooltip>, but here the annotated elements can be nested. The most deeply nested tooltip is shown.
Це як задача <info:task/behavior-tooltip>, але тут елементи можуть бути вкладеними. Ви маєте показати найбільш глибоко вкладену підказку.

Only one tooltip may show up at the same time.
Одночасно може відображатися лише одна підказка.

For instance:
Наприклад:

```html
<div data-tooltip="Hereis the house interior" id="house">
<div data-tooltip="Hereis the roof" id="roof"></div>
<div data-tooltip="Осьінтер’єр будинку" id="house">
<div data-tooltip="Осьдах" id="roof"></div>
...
<a href="https://en.wikipedia.org/wiki/The_Three_Little_Pigs" data-tooltip="Read on…">Hover over me</a>
<a href="https://uk.wikipedia.org/wiki/Троє_поросят" data-tooltip="Читайте далі…">Наведіть на мене вказівник</a>
</div>
```

The result in iframe:
Результат в iframe:

[iframe src="solution" height=300 border=1]
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@

The algorithm looks simple:
1. Put `onmouseover/out` handlers on the element. Also can use `onmouseenter/leave` here, but they are less universal, won't work if we introduce delegation.
2. When a mouse cursor entered the element, start measuring the speed on `mousemove`.
3. If the speed is slow, then run `over`.
4. When we're going out of the element, and `over` was executed, run `out`.
Алгоритм виглядає просто:
1. Додайте обробники `onmouseover/out` на елемент. Тут також можна використовувати `onmouseenter/leave`, але вони менш універсальні, і не працюватимуть, якщо ми використаємо делегування подій.
2. Коли вказівник миші увійшов на елемент, почніть вимірювати швидкість на `mousemove`.
3. Якщо швидкість низька, то запускаємо `over`.
4. Коли вказівник виходить за межі елемента, і `over` закінчила свою роботу, запускаємо `out`.

But how to measure the speed?
Але як виміряти швидкість?

The first idea can be: run a function every `100ms` and measure the distance between previous and new coordinates. If it's small, then the speed is small.
Перша ідея може бути такою: запускати функцію кожні `100ms` і вимірювати відстань між попередньою та новою координатами. Якщо відстань маленька, то швидкість невелика.

Unfortunately, there's no way to get "current mouse coordinates" in JavaScript. There's no function like `getCurrentMouseCoordinates()`.
На жаль, у JavaScript немає способу отримати поточні координати вказівника миші. Немає таких функцій, як `getCurrentMouseCoordinates()`.

The only way to get coordinates is to listen for mouse events, like `mousemove`, and take coordinates from the event object.
Єдиний спосіб отримати координати -- в обробнику подій миші, наприклад `mousemove`, і брати координати з об’єкта події.

So let's set a handler on `mousemove` to track coordinates and remember them. And then compare them, once per `100ms`.
Отже, давайте додамо обробник на `mousemove`, де відстежемо і запам'ятаємо поточні координати. А далі будемо порівнювати їх раз на `100ms`.

P.S. Please note: the solution tests use `dispatchEvent` to see if the tooltip works right.
P.S. Зверніть увагу: тести рішення використовують `dispatchEvent`, щоб перевірити, чи підказка працює правильно.
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
class HoverIntent {

constructor({
sensitivity = 0.1, // speed less than 0.1px/ms means "hovering over an element"
interval = 100, // measure mouse speed once per 100ms
sensitivity = 0.1, // швидкість менше 0,1 пікселів/мс означає "наведення вказівника на елемент"
interval = 100, // вимірювати швидкість миші раз на 100ms: обчислити відстань між попередньою та наступною точками
elem,
over,
out
Expand All @@ -15,12 +15,12 @@ class HoverIntent {
this.over = over;
this.out = out;

// make sure "this" is the object in event handlers.
// переконайтеся, що "this" є нашми об’єктом в обробниках подій.
this.onMouseMove = this.onMouseMove.bind(this);
this.onMouseOver = this.onMouseOver.bind(this);
this.onMouseOut = this.onMouseOut.bind(this);

// and in time-measuring function (called from setInterval)
// і у функції вимірювання часу (викликається з setInterval)
this.trackSpeed = this.trackSpeed.bind(this);

elem.addEventListener("mouseover", this.onMouseOver);
Expand All @@ -32,16 +32,16 @@ class HoverIntent {
onMouseOver(event) {

if (this.isOverElement) {
// if we're over the element, then ignore the event
// we are already measuring the speed
// якщо ми знову пройшли над елементом, ігноруємо подію,
// бо ми вже вимірюємо швидкість
return;
}

this.isOverElement = true;

// after every mousemove we'll be check the distance
// between the previous and the current mouse coordinates
// if it's less than sensivity, then the speed is slow
// після кожного руху миші ми будемо перевіряти відстань
// між попередньою та поточною координатами миші
// якщо ця відстань менше ніж значення sensitivity, швидкість повільна

this.prevX = event.pageX;
this.prevY = event.pageY;
Expand All @@ -52,13 +52,13 @@ class HoverIntent {
}

onMouseOut(event) {
// if left the element
// якщо залишили елемент
if (!event.relatedTarget || !elem.contains(event.relatedTarget)) {
this.isOverElement = false;
this.elem.removeEventListener('mousemove', this.onMouseMove);
clearInterval(this.checkSpeedInterval);
if (this.isHover) {
// if there was a stop over the element
// якщо була зупинка над елементом
this.out.call(this.elem, event);
this.isHover = false;
}
Expand All @@ -76,7 +76,7 @@ class HoverIntent {
let speed;

if (!this.lastTime || this.lastTime == this.prevTime) {
// cursor didn't move
// вказівник не рухався
speed = 0;
} else {
speed = Math.sqrt(
Expand All @@ -90,7 +90,7 @@ class HoverIntent {
this.isHover = true;
this.over.call(this.elem);
} else {
// speed fast, remember new coordinates as the previous ones
// якщо рухається швидко, записуємо нові координати як попередні
this.prevX = this.lastX;
this.prevY = this.lastY;
this.prevTime = this.lastTime;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
'use strict';

// Here's a brief sketch of the class
// with things that you'll need anyway
// Ось короткий нарис класу
// з речами, які все одно знадобляться
class HoverIntent {

constructor({
sensitivity = 0.1, // speed less than 0.1px/ms means "hovering over an element"
interval = 100, // measure mouse speed once per 100ms: calculate the distance between previous and next points
sensitivity = 0.1, // швидкість менше 0,1 пікселів/мс означає "наведення вказівника на елемент"
interval = 100, // вимірювати швидкість миші раз на 100ms: обчислити відстань між попередньою та наступною точками
elem,
over,
out
Expand All @@ -17,16 +17,16 @@ class HoverIntent {
this.over = over;
this.out = out;

// make sure "this" is the object in event handlers.
// переконайтеся, що "this" є нашим об’єктом в обробниках подій.
this.onMouseMove = this.onMouseMove.bind(this);
this.onMouseOver = this.onMouseOver.bind(this);
this.onMouseOut = this.onMouseOut.bind(this);

// assign the handlers
// додамо обробники
elem.addEventListener("mouseover", this.onMouseOver);
elem.addEventListener("mouseout", this.onMouseOut);

// continue from this point
// ми зробили що могли, далі ви вже самі ;)

}

Expand All @@ -44,8 +44,8 @@ class HoverIntent {


destroy() {
/* your code to "disable" the functionality, remove all handlers */
/* it's needed for the tests to work */
/* ваш код, щоб "вимкнути" функціональність, видаліть усі обробники */
/* це потрібно для роботи тестів */
}

}
Loading