پیمایش در عناصر DOM

06 اردیبهشت 1398
Advanced-Javascript-dom-navigation

پیمایش در DOM

زمانی که از پیمایش (به انگلیسی: Navigation) صحبت میکنیم منظورمان رسیدن به عناصر مختلفِ DOM با استفاده از دستورات جاوا اسکریپت است. بگذارید بیشتر توضیح دهم. بر اساس اعلام کنسرسیوم جهانی وب (W3C) هر چیزی که در HTML است یک node (به معنی «گره») محسوب می شود، یعنی:

  • تمام سند HTML، یک node یا گره سندی است
  • تمام عناصر HTML، یک node عنصری است
  • متن درون عناصر HTML، یک node متنی است
  • هر Attribute، یک node یا گرهِ Attribute ای است (البته این مورد منسوخ شده است)
  • تمام comment ها نیز node کامنتی هستند
درخت HTML
درخت HTML

هر چیزی که در درخت HTML مشاهده می کنید یک node است. node ها می توانند حذف شوند، ویرایش شوند و یا از نو ساخته شوند.

این node ها، همانطور که در تصویر مشاهده می کنید، با یکدیگر یک نوع رابطه ی سلسله مراتبی دارند که هر یک زیر مجموعه ی دیگری قرار می گیرد. برای توصیف این رابطه ی سلسله مراتبی از لغاتی استفاده می کنیم که بهتر است همینجا آن ها را توضیح دهم:

  • root (به معنی «ریشه») بالاترین node در یک سند محسوب می شود
  • هر node، به غیر از root، یک پدر (در انگلیسی می گویند parent) دارد. node پدر یعنی node ای که بقیه ی node ها در آن قرار گرفته باشند.
  • هر node می تواند تعدادی فرزند (در انگلیسی می گویند children) داشته باشد. فرزند های یک node یعنی node هایی یا عناصری که داخل آن هستند.
  • node های خواهر و برادر (در انگلیسی Sibling) در واقع node هایی هستند که یک پدر (parent) داشته باشند.

به کد زیر نگاه کنید:

<html>

  <head>
    <title>DOM Tutorial</title>
  </head>

  <body>
    <h1>DOM Lesson one</h1>
    <p>Hello world!</p>
  </body>

</html>
نمود تصویری کد بالا
نمود تصویری کد بالا

از نگاه کردن به کد و نمود تصویری آن متوجه می شویم که:

  • <html> همان root است (بالاتر از دیگر node ها است)
  • <html> هیچ پدری ندارد
  • <html> خودش پدرِ <head> و <body> است
  • <head> اولین فرزند (first child) عنصرِ <html> است
  • <body> آخرین فرزند (last child) عنصرِ <html> است

همچنین:

  • <head> یک فرزند دارد -> <title>
  • <title> یک فرزند دارد که یک node متنی است -> "DOM Tutorial"
  • <body> دو فرزند دارد -> <h1> و <p>
  • <h1> یک فرزند دارد -> "DOM Lesson one"
  • <p> یک فرزند دارد -> "!Hello world"
  • <h1> و <p> خواهر و برادر (sibling) هستند

برای پیمایش و حرکت بین این node ها از دستورات زیر استفاده می شود:

  • parentNode
  • childNodes[nodenumber]
  • firstChild
  • lastChild
  • nextSibling
  • previousSibling

بیایید با این دستورات آشنا شویم.

تفاوت node عنصری و متنی

به کد زیر نگاه کنید:

<title id="demo">DOM Tutorial</title>

در مثال بالا یک node عنصری داریم (<title>) که شامل هیچ متنی نیست! بلکه شامل یک node متنی است و آن node متنی مقدارش برابر با "DOM Tutorial" است! معمولا برنامه نویسان تازه کار این مسئله را متوجه نمی شوند که node عنصری با node متنی تفاوت دارد.

حالا اگر بخواهیم به مقدار این node متنی دسترسی داشته باشیم باید از خصوصیت innerHTML استفاده کنیم:

var myTitle = document.getElementById("demo").innerHTML;

اگر یادتان باشد گفته بودیم که innerHTML یعنی محتوای درون یک تگ یا عنصر HTML. بنابراین استفاده از innerHTML برای دسترسی به محتوای درونی مانند استفاده از nodeValue برای اولین فرزند در این مثال است:

var myTitle = document.getElementById("demo").firstChild.nodeValue;

در این دستور ابتدا می گوییم فرزند اول را بگیر (که یک node متنی است) سپس nodeValue یا همان مقدارِ node را به ما بده.

دسترسی پیدا کردن به فرزند اول می تواند به این شکل نیز انجام شود:

var myTitle = document.getElementById("demo").childNodes[0].nodeValue;

میبینید که در این روش انگار از index آرایه ها استفاده کرده ایم.

در زیر سه مثال مختلف برایتان آورده ایم که هر سه یک کار واحد را انجام می دهند؛ آن ها متن <h1> را میگیرند و سپس آن را در <p> کپی می کنند. این کار را انجام داده ایم تا شما با روش های مختلف کار با node ها آشنا شوید.

مثال اول:

<!DOCTYPE html>
<html>
<body>

<h1 id="id01">My First Page</h1>
<p id="id02"></p>

<script>
document.getElementById("id02").innerHTML = document.getElementById("id01").innerHTML;
</script>

</body>
</html>

مشاهده ی خروجی در JSBin

در این مثال از همان روش ساده ی innerHTML استفاده کرده ایم.

مثال دوم:

<!DOCTYPE html>
<html>
<body>

<h1 id="id01">My First Page</h1>
<p id="id02"></p>

<script>
document.getElementById("id02").innerHTML = document.getElementById("id01").firstChild.nodeValue;
</script>

</body>
</html>

مشاهده ی خروجی در JSBin

در این مثال از پیمایش استفاده کرده ایم. یعنی ابتدا node فرزند را پیدا کرده و سپس محتوایش را گرفته ایم.

مثال سوم:

<!DOCTYPE html>
<html>
<body>

<h1 id="id01">My First Page</h1>
<p id="id02"></p>

<script>
document.getElementById("id02").innerHTML = document.getElementById("id01").childNodes[0].nodeValue;
</script>

</body>
</html>

مشاهده ی خروجی در JSBin

در این روش هم به شکل index های آرایه ای کار کرده ایم.

تفاوتی در این روش ها وجود ندارد و همه بستگی به موقعیت کار و سلیقه ی شما دارد. از آنجا که روش innerHTML ساده تر از دیگر روش هاست ما در این مقاله از همین روش استفاده می کنیم.

دسترسی به Root

برای دسترسی پیدا کردن به Root دو روش اصلی وجود دارد:

  • استفاده از دستور document.body که بدنه ی سند را به ما می دهد
  • استفاده از دستور document.documentElement که کل سند را به ما می دهد

به دو مثال زیر توجه کنید؛

مثال اول:

<!DOCTYPE html>
<html>
<body>

<p>Hello World!</p>

<div>
<p>The DOM is very useful!</p>
<p>This example demonstrates the <b>document.body</b> property.</p>
</div>

<script>
alert(document.body.innerHTML);
</script>

</body>
</html>

مشاهده ی خروجی در JSBin

در این مثال به دلیل استفاده از دستور document.body، تنها بدنه ی سند به ما داده شده است. یعنی آنچه که در <body> قرار می گیرد و کد های <head> را نداریم.

مثال دوم:

<!DOCTYPE html>
<html>
<body>

<p>Hello World!</p>

<div>
<p>The DOM is very useful!</p>
<p>This example demonstrates the <b>document.documentElement</b> property.</p>
</div>

<script>
alert(document.documentElement.innerHTML);
</script>

</body>
</html>

مشاهده ی خروجی در JSBin

در این مثال از دستور document.documentElement استفاده کرده ایم و مشاهده می کنید که تمام کدهای صفحه را به ما داده است

دستورات nodeName nodeValue و nodeType

دستور nodeName نام یک node را مشخص می کند و:

  • nodeName از نوع read-only است؛ یعنی تنها می تواند مقادیر را بخواند و نمی تواند هیچ چیزی را تغییر دهد
  • nodeName ای که مربوط به یک node عنصری باشد در واقع همان نام تگ است
  • nodeName ای که مربوط به یک node از نوع Attribute باشد در واقع همان نام attribute است
  • nodeName ای که مربوط به یک node متنی باشد، همیشه text# است
  • nodeName ای که مربوط به یک node سندی باشد، همیشه document# است

مثال:

<!DOCTYPE html>
<html>
<body>

<h1 id="id01">My First Page</h1>
<p id="id02"></p>

<script>
document.getElementById("id02").innerHTML = document.getElementById("id01").nodeName;
</script>

</body>
</html>

مشاهده ی خروجی در JSBin

نکته: nodeName همیشه نام تگ ها را به صورت uppercase (حروف بزرگ انگلیسی) ارائه میدهد.


دستور nodeValue مقدار یک node را برمیگرداند و:

  • nodeValue مربوط به یک node عنصری null است
  • nodeValue مربوط به یک node متنی، خودِ متن است
  • nodeValue مربوط به یک node از نوع attribute مقدارِ آن attribute است

دستور nodeType نیز نوع یک node را بر میگرداند و read-only است. مثال:

<!DOCTYPE html>
<html>
<body>

<h1 id="id01">My First Page</h1>
<p id="id02"></p>

<script>
document.getElementById("id02").innerHTML = document.getElementById("id01").nodeType;
</script>

</body>
</html>

مشاهده ی خروجی در JSBin

مقدار برگردانده شده در مثال بالا عدد 1 است! برای اینکه بفهمید 1 به چه معنی است باید به جدول زیر نگاهی بیندازید:

Node نوع مثال
ELEMENT_NODE 1 <h1 class="heading">Roxo</h1>
ATTRIBUTE_NODE 2 class = "heading" (deprecated)
TEXT_NODE 3 Roxo
COMMENT_NODE 8 <!-- This is a comment -->
DOCUMENT_NODE 9 The HTML document itself (the parent of <html>)
DOCUMENT_TYPE_NODE 10 <!Doctype html>

نکته: عدد 2 که مربوط به attribute ها می باشد در HTML منسوخ شده است اما هنوز هم کار می کند. البته این مورد در XML DOM منسوخ نشده است.

امیدوارم از این قسمت لذت برده باشید.

تمام فصل‌های سری ترتیبی که روکسو برای مطالعه‌ی دروس سری جاوا اسکریپت پیشرفته توصیه می‌کند:
نویسنده شوید

دیدگاه‌های شما (1 دیدگاه)

در این قسمت، به پرسش‌های تخصصی شما درباره‌ی محتوای مقاله پاسخ داده نمی‌شود. سوالات خود را اینجا بپرسید.

حسن فرجی
14 مهر 1398
سلام با Node متنی میشه خصوصیت (css (style داده ؟ اگه اره ممنون میشم مثال بزنید اگه نه روش جایگزین با js بگین

در این قسمت، به پرسش‌های تخصصی شما درباره‌ی محتوای مقاله پاسخ داده نمی‌شود. سوالات خود را اینجا بپرسید.

امیر زوارمی
18 مهر 1398
سلام دوست عزیز، Node های متنی چیزی هست که بین دو تگ HTML قرار میگیره همونطور که در مثال بالا دیدیم. برای اضافه کردن استایل های css به چند صورت میشه عمل کرد. روش inline: با کلیدواژه ی style کار انجام میشه: document.getElementById('target').style.color = 'tomato' روش استایل های سراسری: توی این روش باید یه تگ style درست کنیم و اون رو به بدنه ی HTML بچسبونیم: var style = document.createElement('style'); style.innerHTML = ` #target { color: blueviolet; } `; document.head.appendChild(style); استفاده از دستور insertRule: var style = document.createElement('style'); document.head.appendChild(style); style.sheet.insertRule('#target {color: darkseagreen}'); روش جدید تر که البته هنوز توی خیلی از مرورگر ها پشتیبانی نمیشه: // Create our shared stylesheet: const sheet = new CSSStyleSheet(); sheet.replaceSync('#target {color: darkseagreen}'); // Apply the stylesheet to a document: document.adoptedStyleSheets = [sheet]; اطلاعات بیشتر و توضیحات کامل توی این مقاله هست: https://dev.to/karataev/set-css-styles-with-javascript-3nl5

در این قسمت، به پرسش‌های تخصصی شما درباره‌ی محتوای مقاله پاسخ داده نمی‌شود. سوالات خود را اینجا بپرسید.