Document Object Model و یا به اختصار DOM به ظهور زبان JavaScript گره خوردهاست. این مدل به همراه یک API پیاده سازی شدهی با JavaScript است که امکان دسترسی به اسناد HTML را مسیر میکند. علاوه بر امکاناتی مانند انتخاب عناصر، کار با ویژگیها و ذخیرهی اطلاعات که تاکنون بررسی کردیم، DOM API به همراه روشهایی برای ایجاد عناصر جدید، حذف عناصر موجود و جابجایی آنها در صفحه میباشد. یکی از مهمترین اهداف jQuery کار سادهتر با DOM است و تعداد متدهایی را که برای کار با DOM ارائه میکند، تاکنون کمتر از 20 درصد کل DOM API اصلی را پوشش میدهند.
حرکت دادن المانها در صفحه
ابتدا قطعه کد HTML زیر را درنظر بگیرید:
<body>
<h2>Flavors</h2>
<ul class="flavors">
<li>chocolate</li>
<li>strawberry</li>
<li>vanilla</li>
</ul>
<h2>Types</h2>
<ul class="types">
<li>frozen yogurt</li>
<li>custard</li>
<li>Italian ice</li>
</ul>
<ul class="unassigned">
<li>rocky road</li>
<li>gelato</li>
</ul>
</body>
میخواهیم با تغییر DOM، به خروجی زیر برسیم که در آن لیستها جابجا، تکمیل و یا خالی شدهاند:
<body>
<h2>Types</h2>
<ul class="types">
<li>frozen yogurt</li>
<li>Italian ice</li>
<li>custard</li>
<li>gelato</li>
</ul>
<h2>Flavors</h2>
<ul class="flavors">
<li>chocolate</li>
<li>vanilla</li>
<li>rocky road</li>
<li>strawberry</li>
</ul>
<ul class="unassigned">
</ul>
</body>
حرکت دادن المانها توسط jQuery var $flavors = $('.flavors');
var $chocolate = $flavors.find('li').eq(0);
var $vanilla = $flavors.find('li').eq(2);
$chocolate.after($vanilla);
به این ترتیب vanilla به بعد از chocolate در لیست flavors منتقل میشود.
در ادامه میخواهیم لیست types را به همراه عنوان آن، به قبل از لیست flavors منتقل کنیم:
var $typesHeading = $('h2').eq(1);
$typesHeading.prependTo('body');
$typesHeading.after($('.types'));
متد prependTo سبب درج عنوان types دقیقا پس از تگ body میشود. سپس لیست types را پس از این عنصر جابجا شده اضافه میکنیم.
سپس در لیست unassigned ابتدا rocky road آنرا یافته و به بالای strawberry در لیست flavors اضافه میکنیم. همچنین gelato آنرا نیز یافته و به انتهای لیست types اضافه خواهیم کرد:
var $unassigned = $('.unassigned');
var $rockyRoad = $unassigned.find('li').eq(0);
var $gelato = $unassigned.find('li').eq(1);
$vanilla.after($rockyRoad);
$gelato.appendTo($('.types'));
حرکت دادن المانها توسط جاوا اسکریپت خالص (سازگار با IE 8.0 به بعد)
در ابتدا میخواهیم المان vanilla را به قبل از المان strawberry حرکت دهیم. برای اینکار میتوان از متد استاندارد insertBefore استفاده کرد:
var flavors = document.querySelector('.flavors');
var strawberry = flavors.children[1];
var vanilla = flavors.children[2];
flavors.insertBefore(vanilla, strawberry);
flavors در اینجا والد نودی است که قرار است جابجا شود. اولین پارامتری که به متد insertBefore ارسال میشود، المانی است که قرار است جابجا شود. دومین پارامتر آن «نود مرجع» است. چون میخواهیم vanilla را قبل از strawberry درج کنیم، المان strawberry نود مرجع خواهد بود.
سپس کار انتقال عنوان لیست types و خود آن به قبل از لیست flavors صورت میگیرد:
var headings = document.querySelectorAll('h2');
var flavorsHeading = headings[0];
var typesHeading = headings[1];
var typesList = document.querySelector('.types');
document.body.insertBefore(typesHeading, flavorsHeading);
document.body.insertBefore(typesList, flavorsHeading);
در اینجا ابتدا عنوان types، به ابتدای document.body منتقل میشود (چون والد این عنوان document.body است، متد insertBefore بر روی آن فراخوانی میشود). سپس میخواهیم خود typesList را نیز حرکت دهیم. به همین جهت نیاز به نود مرجع عنوان flavors است که به عنوان پارامتر دوم متد insertBefore ذکر شدهاست تا این لیست، پیش از آن درج شود.
در آخر میخواهیم آیتمهای لیست unassigned را به لیستهای مرتبط با آنها انتقال دهیم:
flavors.insertBefore(document.querySelector('.unassigned > li'), strawberry);
document.querySelector('.types').appendChild(document.querySelector('.unassigned > li'));
در اولین سطر، querySelector تعریف شده، اولین المان لیست یا همان rocky road را بازگشت میدهد. به این ترتیب المان rocky road لیست unassigned به لیست flavors منتقل میشود . به همین جهت flavors به عنوان والد متد insertBefore تعریف شدهاست. نود مرجع نیز strawberry است؛ زیرا میخواهیم rocky road را به پیش از آن منتقل کنیم.
در سطر دوم، چون هم اکنون المان rocky road از لیست unassigned حذف شدهاست، متد querySelector فراخوانی شده، اولین عنصر لیست یا همان gelato را بازگشت میدهد. این المان را توسط متد appendChild به انتهای لیست types اضافه خواهیم کرد. متد appendChild نیز همانند متد insertBefore نیاز به یک والد دارد که همان عنصری است که قرار است المانها به آن افزوده شوند.
کپی کردن المانها <ol class="numbers">
<li>one</li>
<li>two</li>
</ol>
در جیکوئری برای تهیهی یک کپی از این المان خواهیم داشت:
// deep clone: return value is an exact copy
$('.numbers').clone();
اگر به این متد پارامتر true نیز ارسال شود، اطلاعات و همچنین رخدادهای منتسب به آن نیز کپی میشوند. البته این کپی فقط شامل اطلاعات تدارک دیده شدهی توسط jQuery API است و نه خارج از آن.
و در جاوا اسکریپت خالص (سازگار با IE 8.0 به بعد) برای کپی کردن المانها دو روش shallow و deep وجود دارد:
// shallow clone: return value is an empty <ol>
document.querySelector('.numbers').cloneNode();
// deep clone: return value is an exact copy of the tree
document.querySelector('.numbers').cloneNode(true);
Shallow clone به معنای کپی المان ol بدون فرزندان آن است. در حالت deep clone المان ol و تمام فرزندان آن با هم کپی میشوند.
باید دقت داشت که متد cloneNode آنچه را که مشاهده میکنید یا همان اصل markup را کپی میکند. بنابراین اگر از طریق جاوا اسکریپت تغییراتی را در آن شیء داده باشید در متد cloneNode لحاظ نمیشود.
بدیهی است المانهای clone شده تا زمانیکه با متدهایی مانند insertBefore و یا appendChild به صفحه اضافه نشوند، در صفحه نمایان نخواهند شد.
ایجاد و حذف المانها
فرض کنید میخواهیم به لیست flavors مثال ابتدای بحث، دو مورد جدید را اضافه کنیم.
روش افزودن المانهای جدید توسط جیکوئری:
var $flavors = $('.flavors');
// add two new flavors
$('<li>pistachio</li>').appendTo($flavors);
$('<li>neapolitan</li>').appendTo($flavors);
و یا حذف یک آیتم موجود توسط جیکوئری:
// remove the "gelato" type
$('.types li:last').remove();
در اینجا last: اصطلاحا یک pseduo-class ابداعی توسط jQuery است که آنچنان کارآیی بالایی هم ندارد.
روش افزودن المانهای جدید توسط جاوا اسکریپت خالص:
var flavors = document.querySelector('.flavors');
// add two new flavors
flavors.insertAdjacentHTML('beforeend', '<li>pistachio</li>')
flavors.insertAdjacentHTML('beforeend', '<li>neapolitan</li>')
و برای حذف آخرین آیتم یک لیست توسط جاوا اسکریپت خالص:
// remove the "gelato" type
document.querySelector('.types li:last-child').remove();
در اینجا last-child: یک CSS3 pseudo-class selector استاندارد است.
روش دیگر انجام اینکار به صورت زیر توسط متد removeChild است:
var gelato = document.querySelector('.types li:last-child');
// remove the "gelato" type
gelato.parentNode.removeChild(gelato);
کار با المانهای متنی
در جیکوئری متد ()text آن امکان دریافت محتوای متنی و همچنین به روز رسانی آنرا میسر میکند:
$('.types li').eq(1).text('italian ice');
در اینجا متن دومین المان لیست types به italian ice با i کوچک به روز رسانی میشود.
در جاوا اسکریپت خالص، دو خاصیت textContent و همچنین innerText برای خواندن و یا به روز رسانی محتوای متنی عناصر مورد استفاده قرار میگیرند. برای مثال معادل قطعه کد جیکوئری فوق که از متد text استفاده میکند با جاوا اسکریپت خالص به صورت زیر است:
document.querySelectorAll('.types li')[1].textContent = 'italian ice';
توسط querySelectorAll تمام liهای types یافت شده و سپس خاصیت textContent دومین عنصر آن با italian ice به روز رسانی شدهاست.
خاصیت innerText هرچند بر روی اینترفیس HTMLElement تعریف شدهاست، اما جزء هیچکدام از استانداردهای وب نیست؛ ولی توسط تمام مرورگرهای امروزی پشتیبانی میشود. در این حالت به روز رسانی متن توسط آن با خاصیت textContent دقیقا یکی است؛ اما خروجی آن برعکس حالتهای قبل، متن رندر شدهی المانها را بازگشت میدهد. برای مثال در اینجا شامل فاصلههای پیش از این المانها در markup نمیشود.
برای مثال این قسمتی از خروجی خاصیت textContent است:
Flavors
chocolate
vanilla
rocky road
strawberry
اما در این همین حالت خروجی innerText به این صورت است:
Flavors
chocolate
vanilla
rocky road
strawberry
کار با محتوای HTML ایی رشتهای
گاهی از اوقات از سرور قطعهای کد HTML ایی را دریافت میکنیم (که هنوز به صورت المان یا المانهای DOM در نیامدهاست) و در سمت کلاینت میخواهیم آنرا به قسمتی از صفحه اضافه کنیم. روش انجام اینکار در jQuery به صورت زیر است:
var container = '<h2>Containers</h2><ul><li>cone</li><li>cup</li></ul>';
$('<div>').html(container).appendTo('body');
ابتدا یک المان div جدید را ایجاد کردهایم. سپس محتوای این div را با اطلاعات دریافتی از سرور مقدار دهی و در آخر آنرا به انتهای body اضافه میکنیم.
روش دریافت محتوای رشتهای HTML قابل ارسال به سرور نیز به صورت زیر است:
var contents = $('body').html();
روش انجام اینکار با جاوا اسکریپت خالص به صورت زیر است:
var div = document.createElement('div');
div.innerHTML = container;
document.body.appendChild(div);
در اینجا با استفاده از متد استاندارد createElement یک div جدید منقطع از DOM را ایجاد و سپس محتوای آنرا توسط خاصیت innerHTML به HTML دریافتی از سرور تنظیم کردهایم. در آخر این المان منقطع را توسط متد appendChild به انتهای document.body افزودهایم.
روش خواندن این محتوای نهایی نیز به صورت زیر است:
var contents = document.body.innerHTML;
در حالت کار با جاوا اسکریپت خالص به خاصیت outerHTML یک المان نیز دسترسی داریم که خواندن و یا به روز رسانی آن، صرفا بر روی خود المان اصلی تاثیر میگذارد؛ اما innerHTML بر روی المانهای فرزند این المان (محتوای آن) تاثیر گذار است.