ویجت جستجوی زنده
راهنمای کامل ویجت جستجوی زندهی WidgetCore؛ کنترلهای عمومی، طرحبندی، کوئری و صفحهبندی، ساختار خروجی، رفتار جاوااسکریپت و کار بدون جاوااسکریپت.
ویجت جستجوی زنده یک فیلد جستجوی AJAX است: کاربر تایپ میکند و نتایج بدون بارگذاری صفحه زیر فیلد (یا در ناحیهی نتایج همین ویجت) ظاهر میشوند. همهی تنظیمات در چهار بخش محتوا و سه بخش استایل قرار دارند.
| مورد | مقدار |
|---|---|
| شناسهی ویجت | wgcr-search |
| عنوان در پنل | جستجوی زنده |
| آیکون در پنل | eicon-search |
| دسته | ویجتهای WidgetCore (wgcr) |
| استایل / اسکریپت | wgcr-search |
| کلاس ریشه | .wgcr-search |
| واژههای کلیدی | search، live، ajax، «جستجو»، «سرچ»، «دوره»، «مقاله» |
بخش «عمومی»
| کنترل | نوع | پیشفرض | توضیح |
|---|---|---|---|
| حالت نمایش نتایج | انتخابی | modal (حالت لیست) | page = حالت نمایش در صفحه |
| متن راهنما | متن | خالی | اگر خالی بماند، بهصورت خودکار پر میشود (پایینتر توضیح داده شده) |
| نمایش تصویر | کلید | بله | بندانگشتی هر نتیجه |
| نمایش خلاصه | کلید | بله | 18 کلمه از متن، با نقطهچین |
| متن «نمایش همه» | متن | «نمایش همه نتایج» | پیوندی که کاربر را به صفحهی نتایج کامل میبرد |
منطق متن راهنما: اگر «متن راهنما» را خالی بگذارید، افزونه بر اساس منبع یکی از دو مقدار پیشفرض را میگذارد — برای منبع course متن «جستجو در دورهها…» و برای بقیهی منابع «جستجو در مقالات…».
حالتهای نمایش
ویجت جستجوی زنده دو حالت نمایش دارد که با کنترل حالت نمایش نتایج در بخش «عمومی» انتخاب میشود. این انتخاب فقط ظاهر را عوض نمیکند؛ رفتار پنل، کنترلهای فعال و حتی کلید Escape را تغییر میدهد.
مقایسهی دو حالت
| ویژگی | حالت لیست (modal) | حالت نمایش در صفحه (page) |
|---|---|---|
| جای نتایج | پنل بازشو زیر فیلد | ناحیهی همیشهباز داخل همان ویجت |
| وضعیت اولیهی پنل | با hidden بسته است | باز است (aria-expanded="true" روی فیلد) |
| بستهشدن با کلیک بیرون | بله | خیر (پنل بخشی از صفحه است) |
رفتار Escape | بستن پنل؛ اگر بسته بود، پاککردن عبارت | پاککردن عبارت جستجو |
| ستونها | همیشه تکستونی | 1 تا 6 ستون (واکنشگرا) |
| کاشی و ارتفاع برابر | غیرفعال | فعال |
| استایل کانتینر نتایج | فقط پسزمینه و گردی پنل | عرض، ارتفاع کمینه/بیشینه، فاصله، پسزمینه، تراز |
| پیمایش بینهایت | داخل پنل (با اسکرول پنل) | نسبت به پنجرهی مرورگر |
| پیوند «نمایش همه» | بعد از نتایج دیده میشود | فقط وقتی عبارت معتبر است |
| کاربری مناسب | هدر، نوار کناری، جستجوی سریع | صفحهی جستجوی اختصاصی، آرشیو، بخش دورهها |
حالت لیست
در این حالت پنل فقط وقتی باز میشود که دستکم دو نویسه تایپ شده باشد و نتیجهای وجود داشته باشد (یا پیامی برای گفتن باشد). با کلیک بیرون از ویجت، پنل بسته میشود.
کاربر روی فیلد کلیک میکند
اگر از قبل عبارت معتبری در فیلد باشد و نتیجهای مانده باشد، پنل باز میشود.
کاربر تایپ میکند
پس از 250 میلیثانیه درخواست فرستاده میشود و پنل باز میشود.
با کیبورد یا ماوس یک نتیجه انتخاب میشود
کلیدهای بالا و پایین بین نتایج حرکت میکنند و
Enterبه همان نتیجه میرود.با کلیک بیرون یا
Escapeپنل بسته میشودEscapeدوم، عبارت داخل فیلد را پاک میکند.
کجا استفاده کنیم: هدر سایت، نوار کناری، بالای آرشیو، یا هر جایی که فضا محدود است و کاربر میخواهد سریع به یک نتیجه برود.
حالت نمایش در صفحه
در این حالت نتایج در یک ناحیهی دائمی زیر فیلد رندر میشوند و پنل هرگز با کلیک بیرون بسته نمیشود؛ چون بخشی از محتوای صفحه است.
ناحیهی نتایج از ابتدا روی صفحه است
میتوانید با «حداقل ارتفاع» در بخش استایل، جای آن را رزرو کنید تا صفحه هنگام آمدن نتایج نپرد.
عبارت تایپ میشود
نتایج داخل همان ناحیه، در شبکهی چندستونی قرار میگیرند.
ستونها واکنشگرا هستند
مقادیر دسکتاپ/تبلت/موبایل بهترتیب پیشفرض 3/2/1 است.
صفحهبندی داخل همان ناحیه است
شمارهها، قبلی/بعدی یا بارگذاری بیشتر، همه زیر نتایج.
کجا استفاده کنیم: صفحهی «جستجوی پیشرفته»، بخش دورهها یا محصولات، و هر صفحهای که میخواهید نتایج مثل یک گرید محتوایی دیده شوند.
ستونها، کاشی و ارتفاع برابر
| تنظیم | اثر |
|---|---|
| ستونها | کلاس wgcr-search--cols-{n} روی ریشه؛ از 3 ستون به بالا شبکهی CSS ساخته میشود |
| کاشی | کلاس wgcr-search--masonry؛ آیتمها با ارتفاع طبیعی خودشان چیده میشوند |
| ارتفاع برابر | کلاس wgcr-search--equal؛ همهی کارتها به اندازهی بلندترین کارت کشیده میشوند |
انتخاب قالب در حالت صفحه
وقتی «انتخاب قالب» روی سفارشی باشد، شناسهی قالب به مسیر REST فرستاده میشود و هر نتیجه با قالب المنتور شما رندر میشود:
قالب را در کتابخانه بسازید
«قالبها → قالبهای ذخیرهشده»؛ قالب باید منتشر شده باشد.
آن را در ویجت انتخاب کنید
«انتخاب قالب» → سفارشی، سپس قالب موردنظر را از فهرست برگزینید.
خروجی را بررسی کنید
کلاس
wgcr-search--tplبه ریشه اضافه میشود و CSS خود قالب یک بار بارگذاری میشود.
جزئیات بیشتر در صفحهی قالبهای المنتور.
تفاوتهای رفتاری که کمتر دیده میشوند
انتخاب با ماوس
- حالت لیست: حرکت ماوس روی یک نتیجه، آن را «انتخابشده» میکند تا با
Enterباز شود. - هر دو حالت با قالب المنتور: وقتی نتایج با قالب سفارشی رندر میشوند (
wgcr-search-item--tpl)، حرکت ماوس هیچ نتیجهای را «انتخابشده» نشانهگذاری نمیکند و استایل انتخاب اعمال نمیشود؛ چون قالب خودش لینک و استایل دارد و تداخل، کلیک را خراب میکند. اگر نتیجهی قالبی لینک نداشته باشد، کلیک روی خود کارت به آدرس آن نتیجه میرود.
فوکوس پس از صفحهبندی
وقتی با کیبورد صفحه را عوض میکنید، بعد از رندر نتایج فوکوس به همان دکمهای برمیگردد که زدهاید (قبلی، بعدی یا شمارهی صفحهی جاری). این کار باعث میشود کاربر کیبوردی جای خودش را در نوار صفحهبندی گم نکند.
اسکرول بینهایت
در حالت «صفحه»، یک عنصر دیدهبان (.wgcr-search-sentinel) انتهای نتایج را با IntersectionObserver نسبت به پنجرهی مرورگر میسنجد؛ یعنی برای بارگذاری بیشتر لازم نیست داخل پنل اسکرول کنید. در حالت لیست، اسکرول خود پنل معیار است.
دسترسپذیری در هر دو حالت
| ویژگی | حالت لیست | حالت نمایش در صفحه |
|---|---|---|
role="combobox" روی فیلد | بله | بله |
aria-expanded و aria-controls | بله | بله |
role="listbox" روی پنل نتایج | بله | بله |
aria-live="polite" برای پیام وضعیت | بله | بله |
پیمایش نتایج با ArrowDown / ArrowUp | بله | بله |
بازکردن نتیجه با Enter | بله | بله |
بستن با Escape | بله | — (چیزی برای بستن وجود ندارد) |
جزئیات کامل در صفحهی دسترسپذیری و کلیدهای کیبورد.
تفاوتهای استایل
| ناحیه | حالت لیست | حالت نمایش در صفحه |
|---|---|---|
| کلاس ریشه | wgcr-search--modal | wgcr-search--page |
| پسزمینهی نتایج | پسزمینهی پنل بازشو | پسزمینهی کانتینر ویجت |
| بیشینهی ارتفاع | 70vh | نامحدود (تابع کانتینر) |
| ستونهای شبکه | 1 | 1 تا 6 |
چون هر دو حالت کلاس پایهی .wgcr-search را دارند، میتوانید یک قانون CSS برای بخشهای مشترک بنویسید و قانونهای اختصاصی هر حالت را با کلاسهای تغییردهنده اضافه کنید.
الگوهای پیشنهادی
| سناریو | تنظیمات |
|---|---|
| جستجوی سریع در هدر | حالت لیست، 5 نتیجه، بدون تصویر، صفحهبندی هیچکدام |
| صفحهی جستجوی اصلی | حالت صفحه، 12 نتیجه، 3 ستون، صفحهبندی شمارهها |
| گرید دورهها | حالت صفحه، منبع course، ستون 3/2/1، کاشی روشن، بارگذاری با اسکرول |
| نتایج با طراحی اختصاصی | حالت صفحه، قالب سفارشی، ستون 2، صفحهبندی قبلی/بعدی |
قالبهای المنتور
بهجای کارت پیشفرض افزونه، میتوانید ظاهر هر نتیجه را با یک قالب ذخیرهشده در کتابخانهی المنتور بسازید. قالب شما برای هر نتیجه رندر میشود و افزونه فقط دادههای آن نتیجه را در حلقه قرار میدهد.
آمادهسازی قالب
یک قالب جدید در کتابخانه بسازید
در پیشخوان: «قالبها → افزودن جدید» و نوع «بخش» (Section) یا «قالب حلقه» را انتخاب کنید.
داخل قالب از ویجتهای حلقهی المنتور استفاده کنید
ویجتهایی مانند «پست حلقه» یا فیلدهای داینامیک عنوان/تصویر/خلاصه، دادههای همان نتیجه را میگیرند.
قالب را منتشر کنید
فقط قالبهای با وضعیت
publishو نوعelementor_libraryقابل استفادهاند؛ پیشنویس در فهرست نمیآید.قالب را در ویجت انتخاب کنید
در ویجت جستجوی زنده: بخش «طرحبندی» → «انتخاب قالب» را روی سفارشی بگذارید و از فهرست «یک قالب انتخاب کنید» همان قالب را انتخاب کنید.
فهرست قالبها با قابلیت جستجو نمایش داده میشود و کنار آن یک پیوند ویرایش قالب هست که مستقیم به کتابخانهی المنتور میرود.
چه اتفاقی هنگام جستجو میافتد
1. درخواست با پارامتر template=<شناسهی قالب> به REST میرود
2. سرور برای هر نتیجه، حلقه را روی همان نوشته میبندد
3. قالب با get_builder_content_for_display() رندر میشود
4. اگر خروجی محتوای قابلمشاهده داشته باشد، در فیلد html همان نتیجه برمیگردد
5. جاوااسکریپت آن را داخل li.wgcr-search-item--tpl میگذارد
بررسی محتوای قابلمشاهده
پیش از آنکه خروجی قالب پذیرفته شود، دو بررسی انجام میشود:
| بررسی | جزئیات |
|---|---|
| متن قابلمشاهده | برچسبهای script و style حذف میشوند و اگر متن باقیمانده خالی نبود، قبول است |
| رسانه | وجود img، svg، picture، video، iframe یا canvas هم کافی است |
| خطا | اگر رندر قالب استثنا بدهد، خروجی خالی در نظر گرفته میشود |
اگر هیچکدام برقرار نباشد، افزونه به کارت پیشفرض برمیگردد تا کاربر نتیجهی خالی نبیند. یعنی یک قالب خراب، جستجو را از کار نمیاندازد.
CSS قالب چگونه بارگذاری میشود
قالبهای المنتور معمولاً همراه خروجی خود <style> و <link rel="stylesheet"> دارند. افزونه اینها را از HTML نتایج بیرون میکشد و جدا مدیریت میکند:
| نوع | رفتار |
|---|---|
<style> | محتوای همهی بلوکها جمعآوری، یکسانسازی و یک بار داخل <style class="wgcr-search-tpl-style"> در پنل نوشته میشود |
<link rel="stylesheet"> | بهعنوان یک استایل واقعی و یک بار در صفحه به <head> اضافه میشود |
| تکراریها | با کلیدِ متن یکسانسازیشده شناسایی و حذف میشوند |
نتیجه: حتی با 12 نتیجه، CSS قالب 12 بار تکرار نمیشود و <link>ها هم بهجای درج شدن بهصورت متن، مانند استایل واقعی رفتار میکنند.
انیمیشنهای ورود و هندلرها
پس از رندر نتایج قالبی، دو کار انجام میشود:
- کلاس
elementor-invisibleاز عنصرها حذف میشود؛ وگرنه عنصرهای دارای انیمیشن ورود نامرئی میماندند. - اگر
window.elementorFrontendموجود باشد، هندلرهای سمت کاربر المنتور روی نتایج تازه اجرا میشوند تا رفتارهایی مثل تب، آکاردئون یا کاروسل داخل قالب کار کنند.
نتیجههای قالبی و تعامل
| رفتار | در نتایج قالبی |
|---|---|
| حرکت ماوس روی نتیجه | نتیجه را «انتخابشده» نمیکند |
| استایل انتخاب/هاور افزونه | اعمال نمیشود (ظاهر کاملاً از قالب شماست) |
کیبورد (بالا/پایین و Enter) | کار میکند و با حلقهی بیرونی مشخص است |
| کلیک روی کارت بدون لینک | به آدرس همان نتیجه میرود |
| کلیک روی لینک/دکمه/فیلد داخل قالب | به همان عنصر واگذار میشود |
الگوی پیشنهادی قالب
یک قالب نتیجهی خوب معمولاً این عنصرها را دارد:
┌──────────────────────────────────────┐
│ تصویر شاخص (16:9) │
│ عنوان (پیوند به نوشته) │
│ خلاصهی کوتاه (2 خط) │
│ رده · تاریخ · نویسنده │
│ دکمهی «ادامه مطلب» │
└──────────────────────────────────────┘
نکات عملی:
- یک لینک اصلی در هر کارت بگذارید؛ لینکهای تودرتو دسترسپذیری را خراب میکنند.
- ارتفاع تصویر را ثابت نگه دارید تا در حالت «کاشی» چیدمان نپرد.
- اگر از «ارتفاع برابر» استفاده میکنید، کارت را با
display:flexوflex-direction:columnبسازید تا دکمه همیشه پایین بماند. - متنهای طولانی را با
line-clampکوتاه کنید؛ افزونه خلاصه را به 18 کلمه محدود میکند ولی قالب شما میتواند کمتر نشان دهد.
رفع مشکلات قالب
| نشانه | علت | راهحل |
|---|---|---|
| نتایج خالیاند و کارت پیشفرض میآید | قالب محتوای قابلمشاهده نساخته | در قالب از فیلدهای داینامیک استفاده کنید، نه متن ثابت خالی |
| ظاهر قالب بدون استایل است | CSS قالب بارگذاری نشده | قالب را یک بار در ویرایشگر باز و ذخیره کنید تا CSS ساخته شود |
| عنصرها نامرئی میمانند | کلاس elementor-invisible | افزونه آن را حذف میکند؛ اگر ماند، انیمیشن ورود را در قالب خاموش کنید |
| فقط اولین نتیجه درست است | حلقهی قالب به دادهی نتیجه وصل نیست | از ویجتهای حلقه/داینامیک المنتور استفاده کنید |
| قالب در فهرست نیست | وضعیت پیشنویس | قالب را منتشر کنید |
پرسوجو و فیلترها
پرسوجوی جستجو از دو لایه ساخته میشود: کنترلهای ویجت (یا ویژگیهای شورتکد) که بدون کدنویسی تنظیم میشوند، و فیلترهای PHP که اجازه میدهند کل کوئری را عوض کنید.
آرگومانهای پایهی کوئری
هر جستجو به یک WP_Query تبدیل میشود که این مقدارهای ثابت را دارد:
| آرگومان | مقدار | چرا |
|---|---|---|
s | عبارت کاربر | جستجوی اصلی وردپرس |
post_type | منبع انتخابی | فقط همان نوع محتوا |
post_status | publish | پیشنویسها دیده نمیشوند |
has_password | false | نوشتههای رمزدار دیده نمیشوند |
posts_per_page | limit (1 تا 50) | سقف نتایج |
paged | max( 1, page ) | صفحهبندی |
no_found_rows | page < 1 | سبکتر شدن کوئری وقتی شمارش لازم نیست |
ignore_sticky_posts | true | ترتیب نتایج به هم نریزد |
فیلترهای بدون کدنویسی
| فیلتر | پارامتر | مقدارها | اثر روی کوئری |
|---|---|---|---|
| مرتبسازی | orderby | relevance, date, title, author, rand, menu_order | relevance هیچ orderby اضافه نمیکند؛ menu_order به menu_order title تبدیل میشود |
| جهت | order | ASC / DESC | فقط وقتی orderby غیر از relevance باشد |
| بازهی تاریخ | date | all, week, month, year | date_query با after و inclusive => true |
| رده/برچسب | terms | [taxonomy:]id با ویرگول | tax_query با رابطهی OR یا AND |
| شامل/استثنا | terms_op | include / exclude | جهت فیلتر ردهها |
| نوشتههای چسبنده | ignore_sticky | yes / no | اگر no باشد، چسبندهها به ابتدای نتایج منتقل میشوند |
| شناسهی کوئری | query_id | کلید ساده | کلید فیلتر اختصاصی سمت سرور |
ساختار tax_query
ترمها بر اساس ردهبندی گروه میشوند و یک tax_query ساخته میشود:
$tax_query = array( 'relation' => $include ? 'OR' : 'AND' );
foreach ( $grouped_terms as $taxonomy => $ids ) {
$tax_query[] = array(
'taxonomy' => $taxonomy,
'field' => 'term_id',
'terms' => $ids,
'operator' => $include ? 'IN' : 'NOT IN',
);
}
یعنی با terms_op="include" کافی است نتیجه در یکی از ردههای دادهشده باشد، و با terms_op="exclude" نتیجه باید از همهی ردههای دادهشده بیرون باشد.
قالب terms
| ورودی | تفسیر |
|---|---|
12 | ردهی پیشفرض (category) با شناسهی 12 |
category:12 | همان مورد، صریح |
post_tag:57,category:12 | دو فیلتر از دو ردهبندی |
category:abc | نادیده گرفته میشود (شناسه باید عدد باشد) |
private_tax:5 | نادیده گرفته میشود (فقط ردهبندی عمومی) |
فیلترهای سمت سرور
دو فیلتر روی آرگومانهای کوئری اعمال میشوند، پیش از اجرای WP_Query:
$args = apply_filters( 'wgcr_search_query_args', $args, $query_id, $request );
if ( '' !== $query_id ) {
$args = apply_filters( 'wgcr_search_query_args/' . $query_id, $args, $request );
}
| فیلتر | چه زمانی | ورودیها |
|---|---|---|
wgcr_search_query_args | همهی جستجوها | $args، $query_id، $request |
wgcr_search_query_args/{query_id} | فقط ویجتهایی با همان شناسه | $args، $request |
فیلتر اختصاصی بعد از فیلتر عمومی اجرا میشود، پس مقدار نهایی با آن تعیین میشود.
کجا بنویسیم
کد نمونه را در یک افزونهی کوچک یا در functions.php پوستهی فرزند بگذارید:
add_filter( 'wgcr_search_query_args/courses', function ( $args, $request ) {
$args['meta_query'] = array(
array(
'key' => 'course_level',
'value' => 'beginner',
'compare' => '=',
),
);
return $args;
}, 10, 2 );
و در ویجت (یا شورتکد) همان شناسه را بگذارید:
[wgcr_search source="course" query_id="courses" mode="page" columns="3"]
نمونههای کاربردی
محدود کردن به یک نویسنده:
add_filter( 'wgcr_search_query_args/team_blog', function ( $args ) {
$args['author__in'] = array( 4, 17, 22 );
return $args;
} );
جستجو در عنوان با وزن بیشتر:
add_filter( 'wgcr_search_query_args', function ( $args, $query_id, $request ) {
if ( 'title' === $request->get_param( 'orderby' ) ) {
$args['orderby'] = 'title';
$args['order'] = 'ASC';
}
return $args;
}, 10, 3 );
حذف یک رده از همهی جستجوها:
add_filter( 'wgcr_search_query_args', function ( $args ) {
$args['tax_query'] = isset( $args['tax_query'] ) ? $args['tax_query'] : array();
$args['tax_query'][] = array(
'taxonomy' => 'category',
'field' => 'slug',
'terms' => array( 'internal' ),
'operator' => 'NOT IN',
);
return $args;
} );
افزودن یک پستتایپ دوم:
add_filter( 'wgcr_search_query_args/library', function ( $args ) {
$args['post_type'] = array( 'post', 'document' );
return $args;
} );
نکات عملکرد
orderby="rand"روی محتواهای بزرگ کند است؛ برای فهرستهای بلند ازdateاستفاده کنید.meta_queryبدون ایندکس مناسب، جستجو را سنگین میکند؛ اگر تعداد نوشتهها زیاد است، کلید متا را ایندکس کنید.date_queryباinclusive => trueساخته میشود، پس «هفته» یعنی از همین لحظه هفت روز به عقب.- اگر به نوار شمارهی صفحه نیاز ندارید،
pageنفرستید تاno_found_rowsفعال بماند و کوئری سبکتر شود.
صفحهبندی
صفحهبندی نتایج جستجو در بخش «صفحهبندی» ویجت تنظیم میشود و فقط وقتی دیده میشود که تعداد نتایج از «آیتم در هر برگه» بیشتر باشد.
چهار نوع صفحهبندی
| نوع | مقدار کنترل | رفتار | مناسب |
|---|---|---|---|
| هیچکدام | none | فقط یک صفحه نتیجه | هدر، جستجوی سریع |
| شمارهها | numbers | نوار کامل با قبلی، بعدی، شمارهها و «صفحه X از Y» | صفحهی جستجوی اصلی |
| قبلی / بعدی | prev_next | فقط دو دکمه | فضاهای باریک |
| بارگذاری با کلیک | load_on_click | دکمهی «بارگذاری بیشتر» نتایج را اضافه میکند | گرید محتوا |
| بارگذاری با اسکرول | load_on_scroll | با رسیدن به انتها، خودکار اضافه میشود | فهرستهای طولانی |
ساختار نوار صفحهبندی
<div class="wgcr-search-foot">
<nav class="wgcr-search-pagination" aria-label="صفحهبندی نتایج">
<button type="button" class="wgcr-search-page wgcr-search-page--prev">قبلی</button>
<button type="button" class="wgcr-search-page" data-page="1">1</button>
<button type="button" class="wgcr-search-page" data-page="2" aria-current="page">2</button>
<span class="wgcr-search-page wgcr-search-page--gap" aria-hidden="true">…</span>
<button type="button" class="wgcr-search-page" data-page="9">9</button>
<button type="button" class="wgcr-search-page wgcr-search-page--next">بعدی</button>
<span class="wgcr-search-page wgcr-search-page--info">صفحه 2 از 9</span>
</nav>
<button type="button" class="wgcr-search-more" aria-controls="wgcr-search-abc123-list" hidden>
<span class="wgcr-search-more-icon" aria-hidden="true">…</span>
<span class="wgcr-search-more-text">مشاهده مقالات بیشتر</span>
</button>
<div class="wgcr-search-nomore" role="status" hidden>نوشتهی بیشتری موجود نیست.</div>
</div>
| کلاس | نقش |
|---|---|
.wgcr-search-pagination | ناوبری صفحهبندی با aria-label |
.wgcr-search-page[data-page] | دکمهی شمارهی صفحه |
.wgcr-search-page--prev و --next | دکمههای قبلی و بعدی |
.wgcr-search-page--gap | نقطهچین بین شمارهها (غیرقابل کلیک) |
.wgcr-search-page--info | متن «صفحه X از Y» |
.wgcr-search-more | دکمهی «بارگذاری بیشتر» |
.wgcr-search-nomore | پیام پایان نتایج با role="status" |
صفحهی جاری با aria-current="page" نشانهگذاری میشود تا صفحهخوان آن را اعلام کند.
کنترلهای مربوط
| کنترل | شناسه | مقدار پیشفرض | توضیح |
|---|---|---|---|
| صفحهبندی | pagination_type | هیچکدام (none) | یکی از چهار نوع بالا |
| چیدمان | pagination_align | وسط (center) | واکنشگرا؛ فقط وقتی صفحهبندی فعال باشد |
| اسپیسر | pagination_spacer | بدون (no) | کلاس wgcr-search--spacer را به ریشه اضافه میکند تا بالای صفحهبندی فاصله بگیرد |
| آیتم در هر برگه | posts_per_page | 12 | در بخش «طرحبندی» (حالت صفحه)؛ 1 تا 50 |
| متن دکمه | button_text | «مشاهده مقالات بیشتر» | متن دکمهی «بارگذاری بیشتر»؛ فقط «بارگذاری با کلیک» |
| آیکن | button_icon | نمایش | آیکون شورون کنار متن دکمه |
| شناسه دکمه | button_id | خالی | اگر پر شود، بهعنوان id دکمه استفاده میشود |
| چیدمان پیام | no_posts_align | وسط (center) | چیدمان پیام پایان نتایج در هر دو نوع بارگذاری |
| پیام سفارشی | custom_message | خیر | برای فعالکردن پیام دلخواه پایان نتایج |
| متن پیام سفارشی | custom_message_text | خالی | جایگزین «نوشتهی بیشتری موجود نیست.» |
چطور تعداد صفحهها محاسبه میشود
وقتی درخواست با پارامتر page فرستاده شود، سرور دو هدر برمیگرداند:
| هدر | محتوا |
|---|---|
X-WP-Total | تعداد کل نتایج (found_posts) |
X-WP-TotalPages | تعداد کل صفحهها (max_num_pages) |
برای اینکه شمارش کل نتایج انجام شود، no_found_rows فقط وقتی page کمتر از 1 باشد روشن میشود؛ یعنی در درخواستهای بدون صفحهبندی، کوئری سبکتر است و شمارش انجام نمیشود.
curl -sI "https://example.com/wp-json/wgcr/v1/search?q=آموزش&limit=12&page=1" | grep -i x-wp
X-WP-Total: 47
X-WP-TotalPages: 4
پیمایش بینهایت
در «بارگذاری با اسکرول»، یک عنصر دیدهبان به نام .wgcr-search-sentinel بعد از آخرین نتیجه قرار میگیرد و با IntersectionObserver دیده میشود:
- در حالت نمایش در صفحه، ملاک دیدهشدن، پنجرهی مرورگر است.
- در حالت لیست، ملاک، اسکرول داخل پنل نتایج است.
- اگر
IntersectionObserverدر مرورگر موجود نباشد، بارگذاری خودکار انجام نمیشود ولی «بارگذاری با کلیک» و شمارهها کار میکنند.
هر بار که صفحهی جدید میرسد، تا پایان رندر منتظر میماند و سپس دیدهبان دوباره فعال میشود؛ پس درخواست پشتسرهم و تکراری فرستاده نمیشود. وقتی صفحهی آخر رسید، پیام پایان نتایج (role="status") نمایش داده میشود و دیدهبان با aria-hidden از دسترس بیرون میرود.
رفتار کیبورد
| کلید | رفتار |
|---|---|
Tab | بین فیلد، نتایج و دکمههای صفحهبندی جابهجا میشود |
Enter روی دکمهی صفحه | همان صفحه بارگذاری میشود و فوکوس روی همان دکمه میماند |
Enter روی «بارگذاری بیشتر» | نتایج بعدی اضافه میشوند |
Escape | در حالت لیست پنل بسته میشود؛ در حالت صفحه عبارت پاک میشود |
پس از رندر صفحهی جدید، فوکوس به دکمهای برمیگردد که زده شده بود: قبلی، بعدی یا دکمهی aria-current="page".
نکات عملکرد
- آیتم در هر برگه را کوچک نگه دارید: مقدار 12 برای بیشتر سایتها مناسب است؛ مقدار 50 در حالت لیست، پنل را طولانی و رندر را سنگین میکند.
- شمارش کل نتایج هزینه دارد: اگر به نوار شمارهها نیاز ندارید، صفحهبندی را «هیچکدام» یا «بارگذاری با کلیک» بگذارید تا
found_postsشمارش نشود. - کش سمت سرور: اگر پروکسی کش هدرهای
X-WP-Totalرا حذف میکند، نوار شمارهها کامل ساخته نمیشود. مسیر/wp-json/را از کش بیرون بگذارید.
انتخاب نوع صفحهبندی
| وضعیت شما | نوع مناسب |
|---|---|
| صفحهی جستجو با نتایج زیاد | شمارهها |
| پنل باریک در سربرگ سایت | قبلی/بعدی یا بارگذاری با کلیک |
| مرور فهرست طولانی بدون کلیک | بارگذاری با اسکرول |
| تعداد کم و ثابت نتایج | هیچکدام |
استایلدهی نتایج
کنترلهای مخصوص حالت «صفحه» فقط وقتی همان حالت انتخاب شده باشد نمایش داده میشوند. راهنمای کامل در صفحهی استایلدهی.
ظاهر ویجت جستجو را میتوانید از سه راه تغییر دهید: کنترلهای استایل المنتور، متغیرهای CSS و بازنویسی کلاسها. این صفحه هر سه را با نمونهکد توضیح میدهد.
متغیرهای CSS
افزونه چند متغیر CSS را روی ریشهی هر .wgcr-search تعریف میکند. این متغیرها سراسری نیستند؛ مقدارهای پایهی رنگ، کادر، گردی و تعداد ستونها را برای همان ویجت فراهم میکنند و میتوانید آنها را روی ریشهی یک نمونه بازنویسی کنید:
| متغیر | مقدار پیشفرض | کاربرد |
|---|---|---|
--wgcr-blue | #2E86DE | رنگ اصلی: حاشیهی فوکوس، آیکون فعال، اسپینر |
--wgcr-navy | #1B5EA8 | حالتهای تیرهتر رنگ اصلی |
--wgcr-ink | #111827 | رنگ متن عنوانها |
--wgcr-slate | #64748B | رنگ متن ثانویه (خلاصه، تاریخ، آیکون) |
--wgcr-line | #E2E8F0 | رنگ حاشیهها |
--wgcr-radius | 14px | گردی فیلد، پنل و کارتها |
--wgcr-search-cols | 1 | تعداد ستونها در حالت «صفحه» |
تغییر کل ظاهر با یک قطعهی کوچک:
.wgcr-search {
--wgcr-blue: #4f46e5;
--wgcr-navy: #3730a3;
--wgcr-ink: #0f172a;
--wgcr-slate: #64748b;
--wgcr-line: #e2e8f0;
--wgcr-radius: 10px;
}
اگر میخواهید فقط یک ویجت خاص عوض شود، همان متغیرها را روی پوشش آن ویجت در المنتور بگذارید (کلاس CSS دلخواه در بخش «پیشرفته»).
کنترلهای استایل المنتور
فیلد جستجو
| کنترل | انتخابگر هدف |
|---|---|
| رنگ متن | .wgcr-search-input |
| رنگ متن راهنما | .wgcr-search-input::placeholder |
| پسزمینه | .wgcr-search-input |
| رنگ کادر (فوکوس) | .wgcr-search-input:focus |
| گردی گوشهها | .wgcr-search-input |
| پدینگ | .wgcr-search-input |
| رنگ و اندازهی آیکون | .wgcr-search-icon |
کادر نتایج
| کنترل | هدف | شرط |
|---|---|---|
| پسزمینه | .wgcr-search-panel | هر دو حالت |
| گردی و پدینگ پنل | .wgcr-search-panel | هر دو حالت |
| عرض کانتینر | .wgcr-search--page .wgcr-search-panel | فقط حالت صفحه |
| حداقل / حداکثر ارتفاع | .wgcr-search--page .wgcr-search-panel | فقط حالت صفحه |
| فاصلهی بیرونی | .wgcr-search--page .wgcr-search-panel | فقط حالت صفحه |
| پسزمینهی کانتینر | .wgcr-search--page .wgcr-search-panel | فقط حالت صفحه |
| تراز افقی | .wgcr-search--page .wgcr-search-panel | فقط حالت صفحه؛ پیشفرض justify |
آیتمها
| کنترل | هدف |
|---|---|
| رنگ عنوان | .wgcr-search-title |
| پسزمینهی آیتم فعال/هاور | .wgcr-search-link:hover و .wgcr-search-item[aria-selected="true"] .wgcr-search-link |
| رنگ خلاصه | .wgcr-search-excerpt |
| رنگ «نمایش همه» | .wgcr-search-all |
| رنگ «نمایش همه» (هاور) | .wgcr-search-all:hover |
چیدمان ستونی، کاشی و ارتفاع برابر
| کلاس ریشه | اثر |
|---|---|
.wgcr-search--page | پنل از حالت مطلق به static تغییر میکند و فهرست به شبکهی CSS تبدیل میشود |
.wgcr-search--cols-1 … --cols-6 | متغیر --wgcr-search-cols را تنظیم میکند |
.wgcr-search--masonry | فهرست از grid به columns تغییر میکند و هر آیتم break-inside: avoid میگیرد |
.wgcr-search--equal | align-items: stretch روی فهرست و height: 100% روی لینک کارت |
.wgcr-search--tpl | نتایج با قالب المنتور رندر شدهاند |
.wgcr-search--spacer | فاصلهی اضافی پیش از ناحیهی صفحهبندی |
.wgcr-search.is-loading | اسپینر داخل فیلد دیده میشود |
ستونها واکنشگرا هستند؛ چون المنتور برای هر نقطهی شکست یک مقدار جدا میسازد:
.wgcr-search--page .wgcr-search-list {
display: grid;
grid-template-columns: repeat(var(--wgcr-search-cols, 1), minmax(0, 1fr));
gap: 4px 10px;
}
نمونهکدهای آماده
کارت با سایهی نرم و لبهی رنگی
.wgcr-search-item .wgcr-search-link {
border: 1px solid var(--wgcr-line);
border-radius: 12px;
box-shadow: 0 1px 3px rgb(15 23 42 / 6%);
transition: box-shadow .18s ease, transform .18s ease;
}
.wgcr-search-item .wgcr-search-link:hover {
box-shadow: 0 10px 24px -8px rgb(15 23 42 / 22%);
transform: translateY(-2px);
}
پنل تیره
.wgcr-search-panel {
background: #0f172a;
border-color: #1e293b;
--wgcr-ink: #f8fafc;
--wgcr-slate: #94a3b8;
}
پنل همعرض با فیلد در حالت لیست
.wgcr-search-panel { inset-inline: 0; }
.wgcr-search--modal .wgcr-search-list { max-height: 60vh; }
حذف تصویر و چیدمان فشرده
.wgcr-search-thumb { display: none; }
.wgcr-search-body { gap: 2px; }
.wgcr-search-excerpt { display: none; }
نکات مهم
- حرکتها با
@media (prefers-reduced-motion: reduce)غیرفعال میشوند؛ اگر انیمیشن دلخواه اضافه میکنید، همین الگو را رعایت کنید. - جهت متن از خود سایت میآید (
inset-inline-startوinset-inline-end)، پس در حالت چپبهراست هم درست کار میکند و نیازی به بازنویسی RTL ندارید. - برچسب فیلد (
.wgcr-search-label) بهصورت بصری پنهان است ولی برای صفحهخوان خوانده میشود؛ آن راdisplay:noneنکنید.
ساختار خروجی
<div class="wgcr-search wgcr-search--modal" data-wgcr-search
data-display-mode="modal" data-source="post" data-limit="12"
data-thumb="yes" data-excerpt="yes" data-all="نمایش همه نتایج"
data-template="0" data-query="…" data-pagination="none">
<form class="wgcr-search-form" role="search" method="get" action="https://example.com/">
<label class="wgcr-search-label" for="wgcr-search-abc123-input">جستجو در مقالات…</label>
<div class="wgcr-search-field">
<span class="wgcr-search-icon"><svg viewBox="0 0 24 24" aria-hidden="true">…</svg></span>
<input type="search" class="wgcr-search-input" id="wgcr-search-abc123-input" name="s"
placeholder="جستجو در مقالات…" autocomplete="off" role="combobox"
aria-autocomplete="list" aria-expanded="false"
aria-controls="wgcr-search-abc123-list" aria-haspopup="listbox">
<input type="hidden" name="post_type" value="post">
<span class="wgcr-search-spinner" aria-hidden="true"></span>
</div>
<div class="wgcr-search-panel" hidden>
<ul class="wgcr-search-list" id="wgcr-search-abc123-list" role="listbox" aria-label="نتایج جستجو"></ul>
<div class="wgcr-search-status" aria-live="polite"></div>
<a class="wgcr-search-all" href="#" hidden>نمایش همه نتایج</a>
</div>
</form>
</div>
هر نتیجه یک li با کلاس wgcr-search-item و role="option" است:
<li class="wgcr-search-item" id="wgcr-search-abc123-opt-0" role="option" aria-selected="false">
<a class="wgcr-search-link" href="https://example.com/?p=42">
<img class="wgcr-search-thumb" src="…" alt="" width="150" height="150" loading="lazy">
<span class="wgcr-search-body">
<span class="wgcr-search-title">عنوان نوشته</span>
<span class="wgcr-search-excerpt">خلاصهی 18 کلمهای…</span>
<span class="wgcr-search-date">2026/09/26</span>
</span>
</a>
</li>
| کلاس | نقش |
|---|---|
.wgcr-search-label | برچسب فیلد (برای صفحهخوان؛ بصری پنهان است) |
.wgcr-search-field | جعبهی فیلد، آیکون و اسپینر |
.wgcr-search-spinner | نشانهی «در حال دریافت» |
.wgcr-search-panel | پنل نتایج؛ در حالت لیست با hidden شروع میشود |
.wgcr-search-list | فهرست نتایج با role="listbox" |
.wgcr-search-status | پیام وضعیت با aria-live="polite" (تعداد نتایج یا خطا) |
.wgcr-search-all | پیوند «نمایش همه نتایج» به جستجوی خود وردپرس |
.wgcr-search-foot | ناحیهی صفحهبندی و دکمهی «بارگذاری بیشتر» |
.wgcr-search-nomore | پیام «نوشتهی بیشتری موجود نیست» با role="status" |
.wgcr-search-item--tpl | نتیجهای که با قالب المنتور رندر شده است |
.wgcr-search-thumb--empty | جای خالی تصویر وقتی نوشته تصویر شاخص ندارد |
پیکربندی جاوااسکریپت
پیش از فایل search.js، یک آبجکت پیکربندی بهصورت درونخطی در صفحه چاپ میشود:
window.WGCRSearch = {
restUrl: 'https://example.com/wp-json/wgcr/v1/search',
homeUrl: 'https://example.com/',
minChars: 2,
maxResults: 50,
i18n: {
noResults: 'نتیجهای یافت نشد.',
found: 'نتیجه',
error: 'خطا در دریافت نتایج.',
prev: 'قبلی',
next: 'بعدی',
page: 'صفحه',
pageOf: 'از'
}
};
| کلید | معنا |
|---|---|
restUrl | نشانی مسیر جستجو، ساختهشده با rest_url() |
homeUrl | نشانی خانهی سایت، برای فرم واقعی جستجو |
minChars | کمترین طول عبارت پیش از ارسال درخواست (2) |
maxResults | سقف سخت آیتمها در هر صفحه (50) |
i18n | متنهای رابط، ترجمهشده به زبان سایت |
رفتار سمت کاربر
| رفتار | مقدار |
|---|---|
| حداقل طول عبارت | 2 نویسه (minChars) |
| تأخیر پس از تایپ (debounce) | 250 میلیثانیه |
| سقف نتایج در هر درخواست | 50 (WGCR_SEARCH_MAX_RESULTS) |
| لغو درخواست قبلی | با AbortController هنگام تایپ سریع |
| نقطهی پایان | /wp-json/wgcr/v1/search |
| پیمایش بینهایت | با IntersectionObserver روی .wgcr-search-sentinel |
جریان کار: کاربر تایپ میکند → 250 میلیثانیه صبر میشود → اگر عبارت کمتر از 2 نویسه بود، پنل خالی و پاک میشود → در غیر این صورت درخواست قبلی لغو و درخواست جدید فرستاده میشود → نتایج رندر میشوند → پیام وضعیت («تعداد نتایج: N») برای صفحهخوان خوانده میشود.
کار بدون جاوااسکریپت
فرم ویجت یک فرم واقعی GET با action برابر خانهی سایت، فیلد s و فیلد پنهان post_type است. یعنی اگر جاوااسکریپت غیرفعال باشد، ارسال فرم کاربر را به صفحهی نتایج جستجوی خود وردپرس برای همان پستتایپ میبرد. این همان الگویی است که برای دسترسپذیری و سئو توصیه میشود: پیشرفت تدریجی، نه وابستگی کامل به اسکریپت.
دادههای روی ریشهی ویجت
جاوااسکریپت تنظیمات را از همان ویژگیهای data-* روی ریشه میخواند؛ پس اگر ویجت را با قالبهای پویا یا کد PHP میسازید، این ویژگیها را کامل بگذارید:
| ویژگی | مثال |
|---|---|
data-wgcr-search | نشانهی راهاندازی (بدون مقدار) |
data-display-mode | modal یا page |
data-source | post، page، product … |
data-limit | 12 |
data-thumb | yes / no |
data-excerpt | yes / no |
data-all | متن پیوند «نمایش همه» |
data-template | شناسهی قالب المنتور یا 0 |
data-query | JSON فشردهی orderby، order، date، terms، terms_op، ignore_sticky، query_id |
data-pagination | none، numbers، prev_next، load_on_click، load_on_scroll |
فهرست کامل کنترلها
همهی کنترلهای محتوایی ویجت به ترتیب بخشهای پنل. کنترلهای استایل در بخش بالا فهرست شدهاند.
| شناسه | برچسب در پنل | نوع | پیشفرض | شرط نمایش |
|---|---|---|---|---|
display_mode | حالت نمایش نتایج | انتخابی | modal (حالت لیست) | — |
placeholder | متن راهنما | متن | خالی (خودکار پر میشود) | — |
thumb | نمایش تصویر | کلید | بله | — |
excerpt | نمایش خلاصه | کلید | بله | — |
all_text | متن «نمایش همه» | متن | «نمایش همه نتایج» | — |
template_mode | انتخاب قالب | انتخابی | default | — |
template | یک قالب انتخاب کنید | انتخاب با جستجو | خالی | template_mode = custom |
edit_template_link | ویرایش قالب | HTML خام (پیوند) | — | template_mode = custom |
columns | ستونها | عدد واکنشگرا | 3 (تبلت 2، موبایل 1) | display_mode = page |
posts_per_page | آیتم در هر برگه | عدد 1 تا 50 | 12 | — |
masonry | کاشی | کلید | خیر | display_mode = page |
equal_height | ارتفاع برابر | کلید | خیر | display_mode = page و masonry خاموش |
source | منبع | انتخابی | post | — |
custom_type | پستتایپ سفارشی | متن | خالی | source = custom |
include_by | شامل توسط | انتخابی | exclude | — |
include_terms | دستهها و برچسبها | انتخاب ترم | خالی | — |
date_filter | تاریخ | انتخابی | all | — |
orderby | ترتیب بر اساس | انتخابی | relevance | — |
order | سفارش | انتخابی | DESC | orderby برابر relevance یا rand نباشد |
ignore_sticky | نادیده گرفتن نوشتههای چسبنده | کلید | بله | source = post |
query_id | کوئری ID | متن | خالی | — |
pagination_type | صفحهبندی | انتخابی | none | — |
pagination_align | چیدمان | انتخابی واکنشگرا | center | pagination_type برابر none نباشد |
pagination_spacer | اسپیسر | انتخابی (با/بدون) | no | pagination_type برابر none نباشد |
button_heading | دکمه | عنوان جداکننده | — | pagination_type = load_on_click |
button_text | متن دکمه | متن | «مشاهده مقالات بیشتر» | pagination_type = load_on_click |
button_icon | آیکن | انتخابی (نمایش/مخفی) | yes | pagination_type = load_on_click |
button_id | شناسه دکمه | متن | خالی | pagination_type = load_on_click |
no_posts_heading | پیام نوشتههای بیشتر موجود نیست | عنوان جداکننده | — | load_on_click یا load_on_scroll |
no_posts_align | چیدمان پیام | انتخابی واکنشگرا | center | load_on_click یا load_on_scroll |
custom_message | پیام سفارشی | کلید | خیر | load_on_click یا load_on_scroll |
custom_message_text | متن پیام سفارشی | ناحیهی متنی | خالی | شرط بالا و custom_message = yes |
رفع مشکلات
| نشانه | علت احتمالی | راهحل |
|---|---|---|
| با تایپ چیزی نمیآید | عبارت کمتر از 2 نویسه | دو نویسه یا بیشتر تایپ کنید |
| پیام «خطا در دریافت نتایج» | REST مسدود یا خطای سرور | curl روی /wp-json/wgcr/v1/search?q=test بزنید |
| نتایج تکراری میآیند | دو ویجت با «شناسه دکمه» یکسان | شناسهی یکتا بگذارید یا خالی بگذارید |
| تصویر نتایج خالی است | نوشته تصویر شاخص ندارد | کلاس .wgcr-search-thumb--empty جای آن را پر میکند؛ یا کنترل «نمایش تصویر» را خاموش کنید |
| ستونها اثر ندارند | حالت نمایش روی لیست است | حالت را به «نمایش در صفحه» تغییر دهید |
| در ویرایشگر کار میکند ولی در سایت نه | کش صفحه یا بهینهساز JS | فایل search.js را از ترکیب/تأخیر خارج کنید |