24 Ocak 2022

Fare Hareketi: mouseover/out, mouseenter/leave

Fare öğeler arasında hareket ettiğinde meydana gelen olaylar hakkında daha fazla ayrıntıya girelim.

Mouseover/mouseout, relatedTarget

mouseover olayı fare bir ögenin üzerine geldiğinde gerçekleşirken, mouseout fare bu ögenin üzerinden gittiği zaman gerçekleşir.

Bu olaylar özeldir, çünkü relatedTarget özellikleri vardır.

Bu özellik targetı (hedefi) tamamlar. Fare bir ögeyi diğer ögeye gitmek için bıraktığında, bunlardan biri target (hedef) , diğeri relatedTarget(ilişkiliTarget) olur.

mouseover için:

  • event.target – fareyle üzerine gelinen öge
  • event.relatedTarget – farenin hedefe gitmeden önce üzerinde oldugu öge relatedTarget (relatedTargettarget).

mouseout için tam tersi:

  • event.target – farenin terk ettiği öge.
  • event.relatedTarget – farenin hedefteki ögeyi terk ettikten sonra gittiği öge. (targetrelatedTarget).

Aşağıdaki örnekte her yüz özelliği bir öğedir. Fareyi hareket ettirdiğinizde, metin alanında fare olaylarını görebilirsiniz.

Her olay, elementin nereden geldiği ve nereye gittiği hakkında bilgi içerir.

Sonuç
script.js
style.css
index.html
container.onmouseover = container.onmouseout = handler;

function handler(event) {

  function str(el) {
    if (!el) return "null"
    return el.className || el.tagName;
  }

  log.value += event.type + ': ' +
    'target=' + str(event.target) +
    ', relatedTarget=' + str(event.relatedTarget) + "\n";
  log.scrollTop = log.scrollHeight;

  if (event.type == 'mouseover') {
    event.target.style.background = 'pink'
  }
  if (event.type == 'mouseout') {
    event.target.style.background = ''
  }
}
body,
html {
  margin: 0;
  padding: 0;
}

#container {
  border: 1px solid brown;
  padding: 10px;
  width: 330px;
  margin-bottom: 5px;
  box-sizing: border-box;
}

#log {
  height: 120px;
  width: 350px;
  display: block;
  box-sizing: border-box;
}

[class^="smiley-"] {
  display: inline-block;
  width: 70px;
  height: 70px;
  border-radius: 50%;
  margin-right: 20px;
}

.smiley-green {
  background: #a9db7a;
  border: 5px solid #92c563;
  position: relative;
}

.smiley-green .left-eye {
  width: 18%;
  height: 18%;
  background: #84b458;
  position: relative;
  top: 29%;
  left: 22%;
  border-radius: 50%;
  float: left;
}

.smiley-green .right-eye {
  width: 18%;
  height: 18%;
  border-radius: 50%;
  position: relative;
  background: #84b458;
  top: 29%;
  right: 22%;
  float: right;
}

.smiley-green .smile {
  position: absolute;
  top: 67%;
  left: 16.5%;
  width: 70%;
  height: 20%;
  overflow: hidden;
}

.smiley-green .smile:after,
.smiley-green .smile:before {
  content: "";
  position: absolute;
  top: -50%;
  left: 0%;
  border-radius: 50%;
  background: #84b458;
  height: 100%;
  width: 97%;
}

.smiley-green .smile:after {
  background: #84b458;
  height: 80%;
  top: -40%;
  left: 0%;
}

.smiley-yellow {
  background: #eed16a;
  border: 5px solid #dbae51;
  position: relative;
}

.smiley-yellow .left-eye {
  width: 18%;
  height: 18%;
  background: #dba652;
  position: relative;
  top: 29%;
  left: 22%;
  border-radius: 50%;
  float: left;
}

.smiley-yellow .right-eye {
  width: 18%;
  height: 18%;
  border-radius: 50%;
  position: relative;
  background: #dba652;
  top: 29%;
  right: 22%;
  float: right;
}

.smiley-yellow .smile {
  position: absolute;
  top: 67%;
  left: 19%;
  width: 65%;
  height: 14%;
  background: #dba652;
  overflow: hidden;
  border-radius: 8px;
}

.smiley-red {
  background: #ee9295;
  border: 5px solid #e27378;
  position: relative;
}

.smiley-red .left-eye {
  width: 18%;
  height: 18%;
  background: #d96065;
  position: relative;
  top: 29%;
  left: 22%;
  border-radius: 50%;
  float: left;
}

.smiley-red .right-eye {
  width: 18%;
  height: 18%;
  border-radius: 50%;
  position: relative;
  background: #d96065;
  top: 29%;
  right: 22%;
  float: right;
}

.smiley-red .smile {
  position: absolute;
  top: 57%;
  left: 16.5%;
  width: 70%;
  height: 20%;
  overflow: hidden;
}

.smiley-red .smile:after,
.smiley-red .smile:before {
  content: "";
  position: absolute;
  top: 50%;
  left: 0%;
  border-radius: 50%;
  background: #d96065;
  height: 100%;
  width: 97%;
}

.smiley-red .smile:after {
  background: #d96065;
  height: 80%;
  top: 60%;
  left: 0%;
}
<!DOCTYPE HTML>
<html>

<head>
  <meta charset="utf-8">
  <link rel="stylesheet" href="style.css">
</head>

<body>

  <div id="container">
    <div class="smiley-green">
      <div class="left-eye"></div>
      <div class="right-eye"></div>
      <div class="smile"></div>
    </div>

    <div class="smiley-yellow">
      <div class="left-eye"></div>
      <div class="right-eye"></div>
      <div class="smile"></div>
    </div>

    <div class="smiley-red">
      <div class="left-eye"></div>
      <div class="right-eye"></div>
      <div class="smile"></div>
    </div>
  </div>

  <textarea id="log">Events will show up here!
</textarea>

  <script src="script.js"></script>

</body>
</html>
relatedTarget null olabilir

relatedTarget özelliği null (boş) olabilir.

Bu normaldir ve sadece farenin başka bir elementten değil, pencerenin dışından geldiği anlamına gelir. Ya da pencereden çıktığı anlamına gelir.

Kodumuzda event.relatedTarget kullanırken bu olasılığı hesaba katmalıyız.event.relatedTarget.tagName özelliğine ulaşırsak, bu hata verebilir.

Ögeleri atlama

mousemove olayı fare hareket ettiğinde tetiklenir. Ancak bu her piksel bir olayı tetikler anlamına gelmez.

Tarayıcı farenin konumunu zaman içerisinde kontrol eder. Ve eğer konum değişikliği farekederse, olayı tetikler.

Bu, eğer kullanıcı fareyi çok hızlı hareket ettiriyorsa, bazı DOM ögelerinin atlanabileceği anlamına gelir.

Eğer fare #FROM ögesinden#TO ögesine çok hızlı bir şekilde hareket ederse, ortadaki <div> ögeleri (ya da bazıları) atlanabilir. mouseout olayı #FROM ogesinde ve ardından aniden #TO ögesi üzerinde tetiklenir.

Bu, performans için iyidir, çünkü arada birçok öge bulunabilir. Her birinde olay tetiklemeyi her zaman istemeyiz.

Öte yandan, fare ımlecinin yol boyunca tüm öğeleri “ziyaret etmediğini” unutmamalıyız. Bu ögelerin üzerinden “zıplayabilir”.

Özellikle, imlecin pencereden sayfanın ortasına doğru atlaması mümkündür. Bu durumda relatedTarget=null, olur çünkü imleç “hiçbir yerden” gelmiştir:

Hızlı fare hareketi durumunda, ortadaki elementler olay tetiklemeyebilirler. Ancak eger fare bır element üzerinde (`mouseover`) olayını tetiklediyse, fare ogeden uzaklastıgında `mouseout` olayının tetikleneceğini garantilemiş oluruz.

Aşağıdaki test standında “canlı” olarak kontrol edebilirsiniz.

HTML iç içe geçmiş <div> ögelerinden oluşuyor. Eğer farenizi onların üzerinden hızlıca hareket ettirirseniz, hiçbir olay tetiklenmeyebilir, belki sadece kırmızı div olay tetikleyebilir ya da sadece yeşil div tetikleyebilir.

Ayrıca, fareyi kırmızı “div” nin üzerine getirmeyi deneyin ve ardından hızlıca yeşil olandan aşağı doğru hareket ettirin. Hareket yeterince hızlıysa, ana öğe göz ardı edilir.

Sonuç
script.js
style.css
index.html
green.onmouseover = green.onmouseout = green.onmousemove = handler;

function handler(event) {
  let type = event.type;
  while (type < 11) type += ' ';

  log(type + " target=" + event.target.id)
  return false;
}


function clearText() {
  text.value = "";
  lastMessage = "";
}

let lastMessageTime = 0;
let lastMessage = "";
let repeatCounter = 1;

function log(message) {
  if (lastMessageTime == 0) lastMessageTime = new Date();

  let time = new Date();

  if (time - lastMessageTime > 500) {
    message = '------------------------------\n' + message;
  }

  if (message === lastMessage) {
    repeatCounter++;
    if (repeatCounter == 2) {
      text.value = text.value.trim() + ' x 2\n';
    } else {
      text.value = text.value.slice(0, text.value.lastIndexOf('x') + 1) + repeatCounter + "\n";
    }

  } else {
    repeatCounter = 1;
    text.value += message + "\n";
  }

  text.scrollTop = text.scrollHeight;

  lastMessageTime = time;
  lastMessage = message;
}
#green {
  height: 50px;
  width: 160px;
  background: green;
}

#red {
  height: 20px;
  width: 110px;
  background: red;
  color: white;
  font-weight: bold;
  padding: 5px;
  text-align: center;
  margin: 20px;
}

#text {
  font-size: 12px;
  height: 200px;
  width: 360px;
  display: block;
}
<!doctype html>
<html>

<head>
  <meta charset="UTF-8">
  <link rel="stylesheet" href="style.css">
</head>

<body>

  <div id="green">
    <div id="red">Test</div>
  </div>

  <input onclick="clearText()" value="Clear" type="button">

  <textarea id="text"></textarea>

  <script src="script.js"></script>

</body>

</html>

Çocuk öge için ayrılırken “Ekstra” mouseout olayı

Fareyle bir elementin üzerine geldiğinizi düşünün. mouseover olayı tetiklendi. Daha sonra fare imleci iç içe geçmiş çocuk elementin üzerine gittiğini varsayalım. İlginç olarak mouseout olayı tetiklenir. Fare imleci teknik olarak hala elementin üzerinde, ancak biz buradan ekstra bir mouseout olayı çıkarmış olduk.

Bu ilginç görünebilir ancak kolayca açıklanabilir bir durumdur.

Tarayıcı mantığına göre, fare imleci herhangi bir zamanda yalnızca tek bir öğenin üzerinde olabilir – en içte olanı seçer ( z-endeksi en yüksek olanı yani en üstte olanı).

Yani başka bir öğeye giderse (aynı ögeye baglı bıle olsa), o zaman bir öncekinden ayrılmış kabul edilir. Bu kadar basit.

Buradan aşağıdaki örnekte görebileceğimiz komik bir sonuç çıkar.

Kırmızı <div> mavi olanın içine konmuştur. Mavi <div> mouseover/out olayı tetiklendiği zamanlarda aşağıdaki metin alanına yazan bir şekilde kodlanmıştır.

Mavi öğeye girip fareyi kırmızı öğenin üzerine getirmeyi deneyin ve olayları izleyin:

Sonuç
script.js
style.css
index.html
function mouselog(event) {
  text.value += event.type + ' [target: ' + event.target.className + ']\n'
  text.scrollTop = text.scrollHeight
}
.blue {
  background: blue;
  width: 160px;
  height: 160px;
  position: relative;
}

.red {
  background: red;
  width: 100px;
  height: 100px;
  position: absolute;
  left: 30px;
  top: 30px;
}

textarea {
  height: 100px;
  width: 400px;
  display: block;
}
<!doctype html>
<html>

<head>
  <meta charset="UTF-8">
  <link rel="stylesheet" href="style.css">
</head>

<body>

  <div class="blue" onmouseover="mouselog(event)" onmouseout="mouselog(event)">
    <div class="red"></div>
  </div>

  <textarea id="text"></textarea>
  <input type="button" onclick="text.value=''" value="Clear">

  <script src="script.js"></script>

</body>

</html>
  1. Mavi ogeye girerken – mouseover [target: blue] olayı tetiklenir.
  2. TMavi olandan kırmızı olana hareket ettikten sonra – mouseout [target: blue] olayı tetiklenir.
  3. …Ve aniden mouseover [target: red].

Bu nedenle, “hedef” i hesaba katmayan bir kod için, “(2)” öğesinde “mouseout” öğesinde üstteki ögeyi bıraktık ve “(3)” öğesinde “mouseover” ile ona geri döndük gibi görünüyor.

Sonuç olarak öğeye girme / öğeden çıkma konusunda bazı eylemler gerçekleştirir, sadece bunları hesaba katarak bir yazılım geliştirirsek birçok ekstra “yanlış” sonuç elde edebiliriz.

Bu sorunu mouseenter/mouseleave olaylarını kullanarak düzeltebiliriz.

mouseenter and mouseleave olayları

mouseenter/mouseleave olayları mouseover/mouseout olaylarına benzer şekilde çalışır. Bunlar da imleç bir ögenin üzerine geldiğinde/ ayrıldığında tetiklenir.

Ancak iki fark vardır:

  1. Elementin içindeki imleç hareketi sayılmaz.
  2. mouseenter/mouseleave olay kabarcıklanması (bubble) yapmaz.

Bu olaylar sezgisel olarak çok açık.

İmleç bir elementin üzerine geldiği zaman – mouseenter tetiklenir, ve bu elementin içinde nereye hareket ettiği önem taşımaz. mouseleave olayı ancak imleç o elementten tamamen ayrıldığı zaman tetiklenir.

Eğer aynı örneği verecek olursak, mouseenter/mouseleave olayını mavi <div> üzerinde denersek, aynı hareketi yaptığımız zaman-- olayların sadece <div> ögesinden girişte ve çıkışta tetiklendiğini görürüz. Ekstra olarak kırmızı olana giriş çıkışta olay tetiklenmez. İç içe geçmiş çocuk elementler göz ardı edilir.

Sonuç
script.js
style.css
index.html
function log(event) {
  text.value += event.type + ' [target: ' + event.target.id + ']\n';
  text.scrollTop = text.scrollHeight;
}
#blue {
  background: blue;
  width: 160px;
  height: 160px;
  position: relative;
}

#red {
  background: red;
  width: 70px;
  height: 70px;
  position: absolute;
  left: 45px;
  top: 45px;
}

#text {
  display: block;
  height: 100px;
  width: 400px;
}
<!DOCTYPE HTML>
<html>

<head>
  <meta charset="utf-8">
  <link rel="stylesheet" href="style.css">
</head>

<body>

  <div id="blue" onmouseenter="log(event)" onmouseleave="log(event)">
    <div id="red"></div>
  </div>

  <textarea id="text"></textarea>
  <input type="button" onclick="text.value=''" value="Clear">

  <script src="script.js"></script>

</body>

</html>

Olay delegasyonu (Event delegation)

mouseenter/leave olaylarının kullanımı çok basittir. Ancak kabarcıklanma (bubble) yapmazlar. Bu yüzden bu olaylarla olay delegasyonu yapamayız.

Tablo hücreleri için fare giriş / çıkışını işlemek istediğimizi hayal edin. Ve yüzlerce hücre var.

Doğal çözüm – işleyiciyi “

” a ayarlamak ve oradaki olayları işlemek olacaktır. Ancak “fare gir / bırak” kabarma yapmayacak. Yani eğer böyle bir olay “
” üzerinde meydana gelirse, o zaman sadece “” üzerindeki bir işleyici onu yakalayabilir.

<table> üzerindeki, mouseenter/leave işleyicileri sadece bütün tabloya girerken/ayrılırken tetiklenir. Tablo içinde olan hareketleri takip etmek imkansızdır.

Bu problem değil – mouseover/mouseout kullanabiliriz.

Basit bir işleyici şöyle görünebilir:

// farenin geçtiği hücreleri ışıklandıralim
table.onmouseover = function(event) {
  let target = event.target;
  target.style.background = 'pink';
};

table.onmouseout = function(event) {
  let target = event.target;
  target.style.background = '';
};
Sonuç
script.js
style.css
index.html
table.onmouseover = function(event) {
  let target = event.target;
  target.style.background = 'pink';
  text.value += "mouseover " + target.tagName + "\n";
  text.scrollTop = text.scrollHeight;
};

table.onmouseout = function(event) {
  let target = event.target;
  target.style.background = '';
  text.value += "mouseout " + target.tagName + "\n";
  text.scrollTop = text.scrollHeight;
};
#text {
  display: block;
  height: 100px;
  width: 456px;
}

#table th {
  text-align: center;
  font-weight: bold;
}

#table td {
  width: 150px;
  white-space: nowrap;
  text-align: center;
  vertical-align: bottom;
  padding-top: 5px;
  padding-bottom: 12px;
}

#table .nw {
  background: #999;
}

#table .n {
  background: #03f;
  color: #fff;
}

#table .ne {
  background: #ff6;
}

#table .w {
  background: #ff0;
}

#table .c {
  background: #60c;
  color: #fff;
}

#table .e {
  background: #09f;
  color: #fff;
}

#table .sw {
  background: #963;
  color: #fff;
}

#table .s {
  background: #f60;
  color: #fff;
}

#table .se {
  background: #0c3;
  color: #fff;
}

#table .highlight {
  background: red;
}
<!DOCTYPE HTML>
<html>

<head>
  <meta charset="utf-8">
  <link rel="stylesheet" href="style.css">
</head>

<body>


  <table id="table">
    <tr>
      <th colspan="3"><em>Bagua</em> Chart: Direction, Element, Color, Meaning</th>
    </tr>
    <tr>
      <td class="nw"><strong>Northwest</strong>
        <br>Metal
        <br>Silver
        <br>Elders
      </td>
      <td class="n"><strong>North</strong>
        <br>Water
        <br>Blue
        <br>Change
      </td>
      <td class="ne"><strong>Northeast</strong>
        <br>Earth
        <br>Yellow
        <br>Direction
      </td>
    </tr>
    <tr>
      <td class="w"><strong>West</strong>
        <br>Metal
        <br>Gold
        <br>Youth
      </td>
      <td class="c"><strong>Center</strong>
        <br>All
        <br>Purple
        <br>Harmony
      </td>
      <td class="e"><strong>East</strong>
        <br>Wood
        <br>Blue
        <br>Future
      </td>
    </tr>
    <tr>
      <td class="sw"><strong>Southwest</strong>
        <br>Earth
        <br>Brown
        <br>Tranquility
      </td>
      <td class="s"><strong>South</strong>
        <br>Fire
        <br>Orange
        <br>Fame
      </td>
      <td class="se"><strong>Southeast</strong>
        <br>Wood
        <br>Green
        <br>Romance
      </td>
    </tr>

  </table>

  <textarea id="text"></textarea>

  <input type="button" onclick="text.value=''" value="Clear">

  <script src="script.js"></script>

</body>
</html>

Bu işleyiciler, herhangi bir öğeden tablonun içindeki herhangi bir diğer ogeye giderken çalışır.

Ancak biz <td> ogelerinin, yalnızca içeri ve dışarı geçişlerini ele almak istiyoruz. Ve hücreleri bir bütün olarak ışıklandırmak istiyoruz. Hücrenin içi veya herhangi bir hücrenin dışı gibi diğer geçişler bizi ilgilendirmez. Onları filtreleyebiliriz.

Çözümlerden biri:

  • Bir değişkende şu anda ışıklandırılmış <td> elementini hatırlayalim. Buna currentElem diyelim .
  • mouseover olayı – eğer hala currentElem içindeysek olayı gözardı et.
  • mouseout olayı – eğer hala currentElem ter etmediysek olayı gözardı et.

Bu, “

” nin çocukları arasında hareket ettiğimizde “ekstra” olayları filtreler.

Bütün detaylarıyla bir örnek verecek olursak:

Sonuç
script.js
style.css
index.html
// <td> under the mouse right now (if any)
let currentElem = null;

table.onmouseover = function(event) {
  if (currentElem) {
    // before entering a new element, the mouse always leaves the previous one
    // if we didn't leave <td> yet, then we're still inside it, so can ignore the event
    return;
  }

  let target = event.target.closest('td');
  if (!target || !table.contains(target)) return;

  // yeah we're inside <td> now
  currentElem = target;
  target.style.background = 'pink';
};


table.onmouseout = function(event) {
  // if we're outside of any <td> now, then ignore the event
  if (!currentElem) return;

  // we're leaving the element -- where to? Maybe to a child element?
  let relatedTarget = event.relatedTarget;
  if (relatedTarget) { // possible: relatedTarget = null
    while (relatedTarget) {
      // go up the parent chain and check -- if we're still inside currentElem
      // then that's an internal transition -- ignore it
      if (relatedTarget == currentElem) return;
      relatedTarget = relatedTarget.parentNode;
    }
  }

  // we left the element. really.
  currentElem.style.background = '';
  currentElem = null;
};
#text {
  display: block;
  height: 100px;
  width: 456px;
}

#table th {
  text-align: center;
  font-weight: bold;
}

#table td {
  width: 150px;
  white-space: nowrap;
  text-align: center;
  vertical-align: bottom;
  padding-top: 5px;
  padding-bottom: 12px;
}

#table .nw {
  background: #999;
}

#table .n {
  background: #03f;
  color: #fff;
}

#table .ne {
  background: #ff6;
}

#table .w {
  background: #ff0;
}

#table .c {
  background: #60c;
  color: #fff;
}

#table .e {
  background: #09f;
  color: #fff;
}

#table .sw {
  background: #963;
  color: #fff;
}

#table .s {
  background: #f60;
  color: #fff;
}

#table .se {
  background: #0c3;
  color: #fff;
}

#table .highlight {
  background: red;
}
<!DOCTYPE HTML>
<html>

<head>
  <meta charset="utf-8">
  <link rel="stylesheet" href="style.css">
</head>

<body>


  <table id="table">
    <tr>
      <th colspan="3"><em>Bagua</em> Chart: Direction, Element, Color, Meaning</th>
    </tr>
    <tr>
      <td class="nw"><strong>Northwest</strong>
        <br>Metal
        <br>Silver
        <br>Elders
      </td>
      <td class="n"><strong>North</strong>
        <br>Water
        <br>Blue
        <br>Change
      </td>
      <td class="ne"><strong>Northeast</strong>
        <br>Earth
        <br>Yellow
        <br>Direction
      </td>
    </tr>
    <tr>
      <td class="w"><strong>West</strong>
        <br>Metal
        <br>Gold
        <br>Youth
      </td>
      <td class="c"><strong>Center</strong>
        <br>All
        <br>Purple
        <br>Harmony
      </td>
      <td class="e"><strong>East</strong>
        <br>Wood
        <br>Blue
        <br>Future
      </td>
    </tr>
    <tr>
      <td class="sw"><strong>Southwest</strong>
        <br>Earth
        <br>Brown
        <br>Tranquility
      </td>
      <td class="s"><strong>South</strong>
        <br>Fire
        <br>Orange
        <br>Fame
      </td>
      <td class="se"><strong>Southeast</strong>
        <br>Wood
        <br>Green
        <br>Romance
      </td>
    </tr>

  </table>

  <script src="script.js"></script>

</body>
</html>

İmleci tablo hücrelerinin içine, dışına ve içlerine taşımaya çalışın. Hızlı veya yavaş olamsı önemli değil. Önceki örnekten farkı burada yalnızca “

” nin bir bütün olarak ışıklandırılmış olmasıdır.

Summary

mouseover, mouseout, mousemove, mouseenter ve mouseleave olaylarını inceledik.

Not edilmesi gerekenler:

  • Hızlı imleç hareketi mouseover, mousemove, mouseout olaylarının ortadaki elementleri atlamasına sebep olabilir.

  • mouseover/out ve mouseenter/leave olaylarının ekstra bir hedef özelliği vardir: relatedTarget. Bu target özelliğini tamamlayıcı olarak işlev görür.

  • mouseover/out olayları anne elementten çocuk elemente hareket ettiğimizde dahi tetiklenir. Tarayıcı bir imlecin aynı anda sadece bir elementin üstünde olabileceğini varsayar – en içteki olanın üzerinde.

  • mouseenter/leave olayları kabarcık (bubble) yapmaz ve imleç daha içteki elementin üzerine gittiğinde tetiklenmez. Sadece imlecin elementin tamamı üzerine girip / ayrılması ile tetiklenirler, diğer öge içinde hareketleri göz ardı ederler.

Görevler

önem: 5

Fare ile bir elementin üzerinden geçildiğinde data-tooltip özelliği ile beraber tooltip (ipucu) gösteren bir javaScript kodu yazın.

Buradaki göreve benziyor Tooltip behavior, ancak burada gösterilen öğeler iç içe geçmiş olabilir. En içteki tooltip gösterilmelidir.

Örneğin:

<div data-tooltip="Here – is the house interior" id="house">
  <div data-tooltip="Here – is the roof" id="roof"></div>
  ...
  <a href="https://en.wikipedia.org/wiki/The_Three_Little_Pigs" data-tooltip="Read on…">Fare ile buranın üzerine gel</a>
</div>

Sonuç iframe üzerinde:

Not ipucu: aynı anda yalnızca bir araç ipucu (tooltip) görünebilir.

Görevler için korunaklı alan aç.

önem: 5

Kullanıcı fareyle bir elementin üzerinden geçtiğinde ancak üzerinden geçip gitmediğinde tooltip (ipucu) gösteren bir fonksiyon yazın.

Diğer kelimelerle, eğer kullanıcı faresiyle bir ögenin üzerine gelirse ve durursa – tooltipi göster. Ancak eğer faresiyle bu ögenin üzerinden hızlıca geçip giderse, tooltip gösterme.

Teknik olarak, bir öğenin üzerindeki fare hızını ölçebiliriz, eğer hızı yavaşsa biz bunu elementin üzerinden geçiyor kabul ederek tooltipi göstermeliyiz. Hızı fazla ise o zaman görmezden gelmeliyiz.

Bunun için global obje new HoverIntent(options) yap. options (seçenekler) ile beraber:

  • elem – Takip edilecek element.
  • over – Eğer fare elementin üzerinden yavaşca geçiyorsa çağırılacak fonksiyon.
  • out – Fare elementin üzerinden ayrıldığı zaman çağırılacak fonksiyon (eğer over çağırıldıysa).

Tooltip için böyle bir objeyi kullanmaya bir örnek:

// örnek tooltip
let tooltip = document.createElement('div');
tooltip.className = "tooltip";
tooltip.innerHTML = "Tooltip";

// mouse hareketlerini takip edecek nesne
new HoverIntent({
  elem,
  over() {
    tooltip.style.left = elem.getBoundingClientRect().left + 'px';
    tooltip.style.top = elem.getBoundingClientRect().bottom + 5 + 'px';
    document.body.append(tooltip);
  },
  out() {
    tooltip.remove();
  }
});

demo:

Fareyi “saat” üzerine hızlı bir şekilde hareket ettirirseniz hiçbir şey olmaz ve bunu yavaş yaparsanız veya durdurursanız, bir tooltip gösterecektir.

Lütfen dikkat: imleç saat alt öğeleri arasında hareket ettiğinde tooltip “gelip gitmez”.

Testler ile korunaklı olan aç.

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. Later if we’re out of the element, and over was executed, run out.

The question is: “How to measure the speed?”

The first idea would be: to run our function every 100ms and measure the distance between previous and new coordinates. If it’s small, then the speed is small.

Unfortunately, there’s no way to get “current mouse coordinates” in JavaScript. There’s no function like getCurrentMouseCoordinates().

The only way to get coordinates is to listen to mouse events, like mousemove.

So we can set a handler on mousemove to track coordinates and remember them. Then we can compare them, once per 100ms.

P.S. Please note: the solution tests use dispatchEvent to see if the tooltip works right.

Çözümü testler korunaklı alanda olacak şekilde aç.

Eğitim haritası