اکنون درباره رویدادهایی که با حرکت موس بین عناصر صفحه اتفاق میافتد، بیشتر صحبت میکنیم.
رویدادهای mouseover/mouseout, relatedTarget
رویداد mouseover
زمانی اتفاق میافتد که اشارهگر موس روی یک عنصر میرود, و mouseout
– زمانی که از روی آن بیرون میرود.
اینها رویدادهای خاصی هستند، زیرا یک خاصیت به نام relatedTarget
دارند. این خاصیت به نوعی مکمل target
خواهد بود. زمانی که اشارهگر موس یک عنصر را با ورودی به عنصر دیگر ترک میکند، یکی از آنها target
، و دیگری relatedTarget
خواهد بود.
برای mouseover
:
event.target
– عنصری خواهدبود که اشارهگر موس روی آن رفتهاست.event.relatedTarget
– عنصری خواهد بود که اشارهگر موس آنرا ترک کرده، به صورت: (relatedTarget
→target
).
برای mouseout
برعکس است:
event.target
– عنصری خواهد بود که اشارهگر موس آنرا ترککرده.event.relatedTarget
– عنصری خواهد بود که زیر اشارهگر موس قرار میگیرد، به صورت: (target
→relatedTarget
).
در مثال زیر هر چهره و اجزای آن عناصر جداگانه هستند. زمانی که اشارهگر موس را روی آنها حرکت دهید، رویدادهایی که اتفاق میافتند را مشاهده خواهید کرد.
هر رویداد اطلاعاتی درباره هر دو target
و relatedTarget
دارد:
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">رویدادها اینجا نشان داده میشوند!
</textarea>
<script src="script.js"></script>
</body>
</html>
relatedTarget
میتواند null
باشدخاصیت relatedTarget
میتواند است null
باشد.
این عادی است و فقط به این معنی است که اشارهگر موس از عنصر دیگری نیامده، بلکه از خارج از پنجره مرورگر وارد شده. یا اینکه با خارج شدن، از پنجره مرورگر خارج شده.
ما باید این احتمال را هنگام استفاده از event.realtedTarget
در کد به یاد داشته باشیم. اگر ما سعی کنیم که event.relatedTarget.tagName
دسترسی پیدا کنیم، با خطا مواجه خواهیم شد.
پرش از روی عناصر
رویداد mousemove
زمانی اتفاق میافتد که اشارهگر موس حرکت کند. اما به این معنی نیست که برای هر پیکسل این رویداد اتفاق بیفتد.
مرورگر موقعیت اشارهگر موس را هرچند وقت بسیار کوتاه یکبار چک میکند. و اگر تغییری را ببیند رویداد را صدا میزند.
به این معنی که اگر بازدیدکننده موس را سریع حرکت دهد، ممکن است بعضی از عناصر داخل DOM از قلم بیفتند:
اگر که موس از #FROM
سریعا به #TO
حرکت کند، مانند شکل بالا، عناصر <div>
وسطی (یا بعضی از آنها) ممکناست از قلم بیفتند. رویداد mouseout
ممکن است روی #FROM
و بعد از آن سریعا mouseover
روی #TO
اتفاق بیفتد.
این برای کارایی برنامه خوب است، چون ممکن است از روی تعداد زیاد عنصر بین دو عنصر عبور کند. در عمل معمولا نمیخواهیم همهای این ورود و خروج از تک تک عناصر را پردازش کنیم.
از طرف دیگر، باید در نظر داشته باشیم که اشارهگر موس همیشه همهی عناصر را در طول مسیر حرکتش “ملاقات” نمیکند. ممکن است “پرش” کند.
در حالت خاصی، ممکن است که اشارهگر موس از بیرون از پنجره مرورگر، دقیقا داخل یک عنصر پرش کند. در این حالت realtedTarget
مقدار null
خواهد داشت، چون اشارهگر عملا از “ناکجا آباد” آمده است:
میتوانید این رفتار را به صورت زنده در قسمت آزمایشی زیر ببینید.
کد اچتیامال دارای دو عنصر تو در تو است: عنصر <div id="child">
داخل عنصر <div id="parent">
قرار گرفته است. اگر که اشارهگر موس را سریعا روی آنها حرکت دهید، ممکن است که فقط عنصر فرزند رویداد را صدا بزند، یا شاید عنصر پدر، حتی ممکن است اصلا رویداد اتفاق نیفتد.
همچنین اشارهگر را داخل div
فرزند ببرید، و سپس آنرا از روی عنصر پدر، با پایین بردن اشارهگر از روی عنصر فرزند خارج کنید. اگر حرکت به اندازه کافی سریع باشد، عنصر پدر نادیده گرفته خواهد شد. اشارهگر موس از روی عنصر پدر بدون توجه به آن عبور میکند.
let parent = document.getElementById('parent');
parent.onmouseover = parent.onmouseout = parent.onmousemove = handler;
function handler(event) {
let type = event.type;
while (type.length < 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;
}
#parent {
background: #99C0C3;
width: 160px;
height: 120px;
position: relative;
}
#child {
background: #FFDE99;
width: 50%;
height: 50%;
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
}
textarea {
height: 140px;
width: 300px;
display: block;
}
<!doctype html>
<html>
<head>
<meta charset="UTF-8">
<link rel="stylesheet" href="style.css">
</head>
<body>
<div id="parent">پدر
<div id="child">فرزند</div>
</div>
<textarea id="text"></textarea>
<input onclick="clearText()" value="Clear" type="button">
<script src="script.js"></script>
</body>
</html>
mouseover
اتفاق بیفتد, حتما mouseout
اتفاق خواهد افتاددر صورت حرکت سریع اشارهگر موس، عناصر بین حرکت ممکن است نادیده گرفتهشوند، اما چیزی که از آن مطمئن هستیم این است که: اگر اشارهگر موس “رسما” وارد یک عنصر شود (رویداد mouseover
اتفاق بیفتند)، هنگام ترک این عنصر همیشه رویداد mouseout
نیز اتفاق خواهد افتاد.
رویداد mouseout وقتی اشارهگر وارد عنصر فرزند میشود
یک ویژگی مهم mouseout
این است که زمانی که از یک عنصر به فرزندان آن برویم، اتفاق میافتد. برای مثال در اچتیامال زیر زمانی که از #parent
به #child
برویم:
<div id="parent">
<div id="child">...</div>
</div>
اگر روی #parent
باشیم و سپس اشارهگر موس را داخلتر و داخل #child
ببریم، رویداد mouseout
روی #parent
اتفاق میافتد.
ممکن است عجیب باشد، اما به سادگی شرح داده میشود.
با توجه به منطق مرورگر، اشاره گر موس این امکان را دارد که فقط بالای یک عنصر در یک زمان واحد باشد. داخلیترین و بالاترین به واسطه z-index.
پس اگر داخل یک عنصر دیگر (حتی فرزند) شود، پس از قبلی بیرون میرود.
به این نکته دیگر درباره جزئیات پردازش رویدادها دقت کنید.
رویداد mouseover
زمانی که روی یک فرزند اتفاق بیفتد، به اطلاح بالا میرود. به این معنی که اگر parent
یک کنترلکننده برای رویداد mouseover
داشته باشد، فراخوانی میشود:
در مثال زیر به خوبی میتوانید ببینید: <div id="child">
داخل <div id="parent">
قرار دارد. برای رویدادهای mouseover/out
کنترلکنندههایی روی #parent
تعریف شده که جزئیاتی درباره رویداد را در خروجی نمایش میدهد.
اگر اشارهگر موس را از روی #parent
حرکت دهید و روی #child
ببرید، دو رویداد را روی #parent
خواهید دید:
mouseout [target: parent]
(ترک پدر), سپسmouseover [target: child]
(ورود به فرزند, اصطلاحا بالا رفته).
function mouselog(event) {
let d = new Date();
text.value += `${d.getHours()}:${d.getMinutes()}:${d.getSeconds()} | ${event.type} [target: ${event.target.id}]\n`.replace(/(:|^)(\d\D)/, '$10$2');
text.scrollTop = text.scrollHeight;
}
#parent {
background: #99C0C3;
width: 160px;
height: 120px;
position: relative;
}
#child {
background: #FFDE99;
width: 50%;
height: 50%;
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
}
textarea {
height: 140px;
width: 300px;
display: block;
}
<!doctype html>
<html>
<head>
<meta charset="UTF-8">
<link rel="stylesheet" href="style.css">
</head>
<body>
<div id="parent" onmouseover="mouselog(event)" onmouseout="mouselog(event)">پدر
<div id="child">فرزند</div>
</div>
<textarea id="text"></textarea>
<input type="button" onclick="text.value=''" value="Clear">
<script src="script.js"></script>
</body>
</html>
همانطور که نشان دادهشد، زمانی که اشارهگر موس از #parent
بیرون و روی #child
میرود، هر دو کنترل کننده پدر فراخوانده میشوند: mouseout
و mouseover
:
parent.onmouseout = function(event) {
/* event.target: عنصر پدر */
};
parent.onmouseover = function(event) {
/* event.target: عنصر فرزند (اصطلاحا بالا رفته) */
};
اگر که event.target
را داخل کنترلکنندهها بررسی نکنیم، به نظر میرسد که موس #parent
را ترک کرده و سپس سریعا روی آن برگشته است.
اما اینگونه نیست! اشارهگر موس هنوز روی عنصر پدر قراردارد، فقط وارد وارد عنصر فرزند که داخلیتر است شده.
اگر زمان خروج از عنصر پدر قرار است، کاری انجام شود، برای مثال انیمیشنی در زمان parent.onmouseout
اجرا شود، معمولا نمیخواهیم که زمانی که موس داخل عناصر داخلیتر #parent
میشود، این کار انجام شود.
برای جلوگیری از این رفتار، ما relatedTarget
را در کنترل کننده چک میکنیم. اگر که اشارهگر موس هنوز داخل عنصر بود، پس صرفا رویداد را نادیده میگیریم.
میتوانیم از دو رویداد دیگر نیز استفاده کنیم: mouseenter
و mouseleave
، که اکنون به آنها میپردازیم، چنین مشکلاتی را باعث نمیشوند.
رویدادهای mouseenter و mouseleave
رویدادهای mouseenter/mouseleave
مشابه mouseover/mouseout
هستند. زمانی اتفاق میافتند که اشارهگر وارد یک عنصر، یا از آن خارج میشود.
اما دو تفاوت اساسی وجود دارد:
- گذرهای داخل عنصر، از\به فرزندها در نظر گرفته نمیشوند.
- رویدادهای
mouseenter/mouseleave
به اصطلاح بالا نمیروند.
این رویدادها بسیار ساده هستند.
زمانی که اشارهگر موش وارد یک عنصر شود، mouseenter
اتفاق میافتد. محل دقیق اشارهگر داخل عنصر یا فرزندهای آن مهم نیست.
زمانی که اشارهگر از یک عنصر خارج شود، mouseleave
اتفاق میافتد.
این مثال شبیه بالایی است، اما عنصر پدر به رویدادهای mouseenter/mouseleave
به جای mouseover/mouseout
گوش میدهد.
همانطور که میبینید، تنها رویدادهایی که ایجاد میشوند،آنهایی هستند که به واسطه ورود و خروج اشارهگر موس از عنصر پدر اتفاق میافتند. زمانی که اشارهگر موس وارد عنصر فرزند و از آن خارج میشود اتفاقی نمیافتد. گذرهایی بین فرزندها اتفاق میافتد نادیده گرفته میشوند.
function mouselog(event) {
let d = new Date();
text.value += `${d.getHours()}:${d.getMinutes()}:${d.getSeconds()} | ${event.type} [target: ${event.target.id}]\n`.replace(/(:|^)(\d\D)/, '$10$2');
text.scrollTop = text.scrollHeight;
}
#parent {
background: #99C0C3;
width: 160px;
height: 120px;
position: relative;
}
#child {
background: #FFDE99;
width: 50%;
height: 50%;
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
}
textarea {
height: 140px;
width: 300px;
display: block;
}
<!doctype html>
<html>
<head>
<meta charset="UTF-8">
<link rel="stylesheet" href="style.css">
</head>
<body>
<div id="parent" onmouseenter="mouselog(event)" onmouseleave="mouselog(event)">پدر
<div id="child">فرزند</div>
</div>
<textarea id="text"></textarea>
<input type="button" onclick="text.value=''" value="Clear">
<script src="script.js"></script>
</body>
</html>
واگذاری رویداد
رویدادهای mouseenter/leave
بسیار ساده و برای استفاده ساده هستند. اما بالا نمیروند. پس نمیتواتیم برای آنها از واگذاری رویدادها استفاده کنیم.
تصور کنید که میخواهیم ورود/خروج اشارهگر موس برای سلولهای جدول کنترل کنیم. و صدها سلول وجود دارد.
طبیعتا راه حلی که ابتدا به ذهن میرسد این است که کنترلکننده را روی <table>
تنظیم کنیم و رویدادها را درون آن پردازش کنیم. اما رویدادهای mouseenter/leave
بالا نمیروند. پس اگر چنین رویدادی روی <td>
اتفاق بیفتد، فقط کنترلکنندهای که روی آن <td>
میتواند متوجه این رویداد شود.
کنترلکنندههایی که برای mouseenter/leave
روی <table>
وجود دارند، تنها زمانی صدا زده میشوند که اشارهگر موس وارد/خارج جدول کلی شود. در این صورت گرفتن اطلاعات درباره گذرهایی که درون خود جدول اتفاق میافتد غیر ممکن خواهد بود.
پس، از mouseover/mouseout
استفاده میکنیم.
با یک کنترلکننده ساده که تنها عنصر زیر اشارهگر موس را مشخص میکند شروع کنیم:
// مشخص کردن عنصر زیر اشارهگر موس
table.onmouseover = function(event) {
let target = event.target;
target.style.background = 'pink';
};
table.onmouseout = function(event) {
let target = event.target;
target.style.background = '';
};
در اینجا میتوانید چیزی که اتفاق میافتد را ببینید. با حرکت اشارهگر موس روی عناصر این جدول، عنصر کنونی که زیر اشارهگر موس است مشخص میشود:
table.onmouseover = function(event) {
let target = event.target;
target.style.background = 'pink';
text.value += `over -> ${target.tagName}\n`;
text.scrollTop = text.scrollHeight;
};
table.onmouseout = function(event) {
let target = event.target;
target.style.background = '';
text.value += `out <- ${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;
cursor: pointer;
}
#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>باگوآ</em> جدول: جهت, عنصر, رنگ, مفهوم</th>
</tr>
<tr>
<td class="nw"><strong>شمال غرب</strong>
<br>فلز
<br>نقره
<br>ریش سفیدان
</td>
<td class="n"><strong>شمال</strong>
<br>آب
<br>آبی
<br>تحول
</td>
<td class="ne"><strong>شمال شرق</strong>
<br>زمین
<br>زرد
<br>جهت
</td>
</tr>
<tr>
<td class="w"><strong>غرب</strong>
<br>فلز
<br>طلا
<br>جوان
</td>
<td class="c"><strong>وسط</strong>
<br>همهچیز
<br>بنفش
<br>توازن
</td>
<td class="e"><strong>شرق</strong>
<br>چوب
<br>آبی
<br>آبی
</td>
</tr>
<tr>
<td class="sw"><strong>جنوب غرب</strong>
<br>زمین
<br>قهوهای
<br>آرامش
</td>
<td class="s"><strong>جنوب</strong>
<br>آتش
<br>نارنجی
<br>سربلندی
</td>
<td class="se"><strong>جنوب شرقی</strong>
<br>چوب
<br>سبز
<br>افسانه
</td>
</tr>
</table>
<textarea id="text"></textarea>
<input type="button" onclick="text.value=''" value="Clear">
<script src="script.js"></script>
</body>
</html>
در این حالت ما میخواستیم که گذرهایی که بین سلولهای جدول <td>
اتفاق میافتد را کنترل کنیم: ورود به یک سلول جدول و خروج از آن. بقیهی گذرها، مانند درون یک سلول، یا خارج هر سلول، به ما کمکی نمیکند. پس بیایید آنها را پالایش کنیم.
کاری که میتوانیم انجام دهیم این است:
- عنصر کنونی مشخص شده
<td>
را درون یک متغیر ذخیره کنیم. آنراcurrentElem
مینامیم. - در هنگام
mouseover
، رویداد را در صورتی که هنوز داخل عنصر<td>
کنونی باشیم، نادیده میگیریم. - در هنگام
mouseout
، رویداد را در صورتی که عنصر<td>
کنونی را ترک نکرده باشیم، نادیده میگیریم.
این مثال تمام حالاتی که ممکن است اتفاق بیفتند حساب کرده است:
// <td> که هم اکنون زیر موس قرار دارد (در صورت وجود)
let currentElem = null;
table.onmouseover = function (event) {
// قبل از ورود به عنصر جدید، اشارهگر موس همیشه قبلی را ترک میکند
// اگر currentElem مقدار داشته باشد، پس ما <td> قبلی را ترک نکردهایم،
// که یعنی یک رویداد mouseover درون آن رخ داده، پس آنرا نادیده میگیریم.
if (currentElem) return;
let target = event.target.closest('td');
// وارد یک <td> نشده پس آنرا نادیده میگیریم
if (!target) return;
// وارد یک <td> شده، اما نه درون جدول مورد نظر ما (در صورتی که جدولهای تو در تو داشته باشیم این امکان وجود دارد)
// نادیده میگیریم
if (!table.contains(target)) return;
// هوورا! ما وارد یک <td> جدید شدیم
currentElem = target;
onEnter(currentElem);
};
table.onmouseout = function (event) {
// اگر که ما اکنون بیرون هر یک از <td> باشیم، پس این رویداد را نادیده میگیریم
// احتمالا اشارهگر موس وارد یک جدول شده اما هنوز بیرون از <td> است،
// مثلا از یک <tr> به <tr> دیگر
if (!currentElem) return;
// ما در حال ترک عنصر هستیم، اما به کجا؟ شاید به یک فرزند؟
let relatedTarget = event.relatedTarget;
while (relatedTarget) {
// زنجیره پدر-فرزندی را بالا میرویم و چک میکنیم که آیا هنوز داخل currentElem هستیم یا نه
// در این صورت این یک گذر داخلی است، که آنرا نادیده میگیریم
if (relatedTarget == currentElem) return;
relatedTarget = relatedTarget.parentNode;
}
// ما واقعا <td> را ترک کردیم
onLeave(currentElem);
currentElem = null;
};
// هر تابعی برای کنترل ورود/خروج یک عنصر
function onEnter(elem) {
elem.style.background = 'pink';
// نمایش آن در textarea
text.value += `over -> ${currentElem.tagName}.${currentElem.className}\n`;
text.scrollTop = 1e6;
}
function onLeave(elem) {
elem.style.background = '';
// نمایش آن در textarea
text.value += `out <- ${elem.tagName}.${elem.className}\n`;
text.scrollTop = 1e6;
}
بار دیگر، ویژگیهای مهم عبارتاند از:
- برای کنترل ورود/خروج اشارهگر موس از هر
<td>
درون جدول، از واگذاری رویداد استفاده میشود. پس ازmouseover/out
بجایmouseenter/leave
برای این منظور استفاده میشود. - رویدادهای اضافی، مانند حرکت موس بین فرزندهای خود
<td>
پالایش شدهاند، به طوری کهonEnter/Leave
تنها زمانی اجرا میشوند که اشارهگر موس وارد/خارج کل<td>
شود.
در اینجا مثال کاملی همراه با تمام جزئیات آمده است:
// <td> که هم اکنون زیر موس قرار دارد (در صورت وجود)
let currentElem = null;
table.onmouseover = function (event) {
// قبل از ورود به عنصر جدید، اشارهگر موس همیشه قبلی را ترک میکند
// اگر currentElem مقدار داشته باشد، پس ما <td> قبلی را ترک نکردهایم،
// که یعنی یک رویداد mouseover درون آن رخ داده، پس آنرا نادیده میگیریم.
if (currentElem) return;
let target = event.target.closest('td');
// وارد یک <td> نشده پس آنرا نادیده میگیریم
if (!target) return;
// وارد یک <td> شده، اما نه درون جدول مورد نظر ما (در صورتی که جدولهای تو در تو داشته باشیم این امکان وجود دارد)
// نادیده میگیریم
if (!table.contains(target)) return;
// هوورا! ما وارد یک <td> جدید شدیم
currentElem = target;
onEnter(currentElem);
};
table.onmouseout = function (event) {
// اگر که ما اکنون بیرون هر یک از <td> باشیم، پس این رویداد را نادیده میگیریم
// احتمالا اشارهگر موس وارد یک جدول شده اما هنوز بیرون از <td> است،
// مثلا از یک <tr> به <tr> دیگر
if (!currentElem) return;
// ما در حال ترک عنصر هستیم، اما به کجا؟ شاید به یک فرزند؟
let relatedTarget = event.relatedTarget;
while (relatedTarget) {
// زنجیره پدر-فرزندی را بالا میرویم و چک میکنیم که آیا هنوز داخل currentElem هستیم یا نه
// در این صورت این یک گذر داخلی است، که آنرا نادیده میگیریم
if (relatedTarget == currentElem) return;
relatedTarget = relatedTarget.parentNode;
}
// ما واقعا <td> را ترک کردیم
onLeave(currentElem);
currentElem = null;
};
// هر تابعی برای کنترل ورود/خروج یک عنصر
function onEnter(elem) {
elem.style.background = 'pink';
// نمایش آن در textarea
text.value += `over -> ${currentElem.tagName}.${currentElem.className}\n`;
text.scrollTop = 1e6;
}
function onLeave(elem) {
elem.style.background = '';
// نمایش آن در textarea
text.value += `out <- ${elem.tagName}.${elem.className}\n`;
text.scrollTop = 1e6;
}
#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;
cursor: pointer;
}
#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>باگوآ</em> جدول: جهت, عنصر, رنگ, مفهوم</th>
</tr>
<tr>
<td class="nw"><strong>شمال غرب</strong>
<br>فلز
<br>نقره
<br>ریش سفیدان
</td>
<td class="n"><strong>شمال</strong>
<br>آب
<br>آبی
<br>تحول
</td>
<td class="ne"><strong>شمال شرق</strong>
<br>زمین
<br>زرد
<br>جهت
</td>
</tr>
<tr>
<td class="w"><strong>غرب</strong>
<br>فلز
<br>طلا
<br>جوان
</td>
<td class="c"><strong>وسط</strong>
<br>همهچیز
<br>بنفش
<br>توازن
</td>
<td class="e"><strong>شرق</strong>
<br>چوب
<br>آبی
<br>آبی
</td>
</tr>
<tr>
<td class="sw"><strong>جنوب غرب</strong>
<br>زمین
<br>قهوهای
<br>آرامش
</td>
<td class="s"><strong>جنوب</strong>
<br>آتش
<br>نارنجی
<br>سربلندی
</td>
<td class="se"><strong>جنوب شرقی</strong>
<br>چوب
<br>سبز
<br>افسانه
</td>
</tr>
</table>
<textarea id="text"></textarea>
<input type="button" onclick="text.value=''" value="Clear">
<script src="script.js"></script>
</body>
</html>
اشارهگر موس را رو و بیرون سلولهای جدول ببرید. سریع یا آسان، اهمیتی ندارد. کل <td>
مشخص خواهد شد، برخلاف مثالی که قبلا دیده بودیم.
خلاصه
ما درباره رویدادهای mouseover
، mouseout
، mousemove
، mouseenter
و mouseleave
صحبت کردیم.
خوب است به این نکات توجه کنیم:
- یک حرکت سریع موس ممکن است باعث پرش از روی عناصر بین حرکت شود.
- رویدادهای
mouseover/out
وmouseenter/leave
یک خاصیت اضافی دارند:relatedTarget
. که عنصری خواهد بود که وارد/خارج آن میشویم، و به نوعی مکملtarget
است.
رویدادهای mouseover/out
زمانی که ما از عنصر پدر به فرزند برویم هم اتفاق میافتند. مرورگر فرض میکند که اشارهگر موس در یک زمان فقط میتواند روی یک عنصر باشد، داخلیترین عنصر.
اما رویدادهای mouseenter/leave
از این نظر متفاوت عمل میکنند: آنها فقط زمانی اتفاق میافتند که اشارهگر موس وارد یا یک عنصر به عنوان یک کل خارج شود. همچنین آنها اصطلاحا، بالا نمیروند.