خلاصه:
یک باکس جستجوی محصول حرفهای برای ووکامرس ، این قطعه کد مجموعهای از قابلیتهای مهم مانند جستجوی زنده Ajax، طراحی مدرن، امنیت بالا، نمایش تصاویر و قیمت محصولات و سازگاری با موبایل را در اختیار شما قرار میدهد.
ساخت باکس جستجو محصول در وردپرس-جستجوی زنده ایجکس
اگر فروشگاه ووکامرسی شما محصولات زیادی دارد، داشتن یک باکس جستجوی ساده دیگر کافی نیست. کاربران امروزی انتظار دارند تنها با تایپ چند حرف، محصولات مرتبط را بهصورت زنده مشاهده کنند. این قطعه کد یک باکس جستجوی حرفهای برای ووکامرس ایجاد میکند که با طراحی مدرن، سرعت بالا و قابلیت جستجوی Ajax تجربه خرید کاربران را به شکل محسوسی بهبود میدهد.
جستجوی زنده محصولات Ajax
یکی از مهمترین قابلیتهای این باکس جستجو، نمایش نتایج بهصورت زنده (Live Search) است. به محض اینکه کاربر حداقل ۳ کاراکتر از نام محصول را وارد کند، درخواست جستجو از طریق Ajax به وردپرس ارسال شده و نتایج بدون رفرش شدن صفحه نمایش داده میشوند. این موضوع علاوه بر افزایش سرعت جستجو، باعث میشود کاربران سریعتر محصول موردنظر خود را پیدا کنند.

طراحی مدرن و ریسپانسیو ایجکس
ظاهر این باکس جستجو کاملاً مدرن طراحی شده است. استفاده از گرادیانت رنگی جذاب، پنجره پاپآپ زیبا، افکت بلور پسزمینه و طراحی حرفهای کارت محصولات باعث شده است تا در موبایل و دسکتاپ عملکرد بسیار مناسبی داشته باشد.
- بی نیاز نصب پلاگین سنگین
نمایش نتایج جستجو بهصورت زنده
بعد از وارد کردن عبارت جستجو، محصولات مرتبط بهصورت کارتهای زیبا نمایش داده میشوند که شامل اطلاعات زیر هستند:
-
تصویر محصول
-
نام محصول
-
قیمت محصول
-
لینک مستقیم محصول
-
لینک مشاهده تمامی نتایج جستجو
این قابلیت باعث میشود کاربران بدون ورود به صفحه جستجوی ووکامرس بتوانند محصولات موردنظر خود را مشاهده و انتخاب کنند.
Ajax برای افزایش سرعت سایت
در این سیستم از تکنولوژی Ajax استفاده شده است. به همین دلیل برای هر جستجو نیازی به بارگذاری مجدد صفحات وجود ندارد.
مزایای این قابلیت عبارتاند از:
-
کاهش مصرف منابع سرور
-
افزایش سرعت تجربه کاربری
-
جلوگیری از بارگذاریهای اضافی صفحات
-
عملکرد روانتر در موبایل و دسکتاپ
- استانداردهای طراحی
دارای 2 شورتکد اختصاصی
این قطعه کد دارای چندین شورتکد مختلف برای استفاده در بخشهای مختلف سایت است.
نمایش آیکون جستجو
[barati_product_search_icon]
[barati_product_search_button]
در این حالت یک دکمه زیبا همراه با آیکون جستجو نمایش داده میشود.- برای جلوگیری از نمایش تعداد زیاد محصولات و افزایش سرعت، امکان تعیین تعداد نتایج وجود دارد.
برای مثال:
limit=”15″
حداقل تعداد نتایج ۳ و حداکثر آن 15 محصول در نظر گرفته شده است که برای اکثر فروشگاههای اینترنتی مقدار کاملاً مناسبی محسوب میشود.
جلوگیری از جستجوی محصولات مخفی ووکامرس
این قطعه کد از قابلیت Product Visibility ووکامرس پشتیبانی میکند. بنابراین محصولاتی که در تنظیمات ووکامرس برای نمایش در نتایج جستجو مخفی شدهاند، به کاربران نمایش داده نخواهند شد.
این ویژگی برای فروشکاههایی که محصولات خصوصی، محصولات ناموجود یا محصولات خاص دارند بسیار کاربردی است.
سازگاری کامل با موبایل
در طراحی این باکس جستجو توجه ویژهای به کاربران موبایل شده است. برخی از امکانات نسخه موبایل عبارتاند از:
-
نمایش صحیح پنجره جستجو
-
جلوگیری از زوم خودکار مرورگر Safari آیفون
-
طراحی بهینه دکمهها
-
اسکرول روان نتایج جستجو
-
نمایش مناسب کارت محصولات
امنیت بالا در جستجوی محصولات
برای افزایش امنیت، در این سیستم از مکانیزمهای امنیتی وردپرس استفاده شده است:
-
استفاده از Nonce برای جلوگیری از حملات جعلی Ajax
-
پاکسازی دادههای ورودی کاربران
-
محدودسازی تعداد نتایج جستجو
-
اعتبارسنجی درخواستهای Ajax
-
استفاده از توابع امن وردپرس و ووکامرس
/**
* Barati Live Product & Post Search Shortcodes
* ------------------------------------
* Code Snippets: بدون <?php
*
* [barati_product_search_icon]
* [barati_product_search_button]
*/
if (!defined('ABSPATH')) {
exit;
}
add_shortcode('barati_product_search_icon', 'blps_shortcode_icon');
add_shortcode('barati_product_search_button', 'blps_shortcode_button');
add_shortcode('barati_product_search', 'blps_shortcode_custom');
add_shortcode('barati_product_search_custom', 'blps_shortcode_custom');
function blps_shortcode_icon($atts) {
$atts = shortcode_atts([
'label' => 'جستجوی سریع',
'title' => 'جستجوی آسان و سریع :',
'placeholder' => 'مثلاً: کیف، کفش، تیشرت...',
'placeholder_post' => 'مثلاً: آموزش، معرفی، راهنما...',
'limit' => 15,
], $atts, 'barati_product_search_icon');
return blps_render_search($atts, 'icon');
}
function blps_shortcode_button($atts) {
$atts = shortcode_atts([
'label' => 'جستجوی سریع',
'title' => 'جستجوی آسان و سریع :',
'placeholder' => 'مثلاً: کیف، کفش، تیشرت...',
'placeholder_post' => 'مثلاً: آموزش، معرفی، راهنما...',
'limit' => 15,
], $atts, 'barati_product_search_button');
return blps_render_search($atts, 'button');
}
function blps_shortcode_custom($atts) {
$atts = shortcode_atts([
'label' => 'جستجوی سریع محصولات',
'title' => 'جستجوی آسان و سریع :',
'placeholder' => 'نام محصول را وارد کن...',
'placeholder_post' => 'نام نوشته را وارد کن...',
'limit' => 15,
], $atts, 'barati_product_search');
return blps_render_search($atts, 'button');
}
function blps_render_search($atts, $trigger_type = 'button') {
$uid = 'mrbarati-coms-' . wp_generate_uuid4();
$ajax_url = admin_url('admin-ajax.php');
$nonce = wp_create_nonce('blps_live_search');
$limit = max(3, min(20, absint($atts['limit'])));
$search_base_product = add_query_arg([
'post_type' => 'product',
's' => '__BLPS_QUERY__',
], home_url('/'));
$search_base_post = add_query_arg([
'post_type' => 'post',
's' => '__BLPS_QUERY__',
], home_url('/'));
ob_start();
?>
<div
id="<?php echo esc_attr($uid); ?>"
class="mrbarati-coms-root mrbarati-coms-trigger-type-<?php echo esc_attr($trigger_type); ?>"
data-ajax="<?php echo esc_url($ajax_url); ?>"
data-nonce="<?php echo esc_attr($nonce); ?>"
data-limit="<?php echo esc_attr($limit); ?>"
data-search-product="<?php echo esc_url($search_base_product); ?>"
data-search-post="<?php echo esc_url($search_base_post); ?>"
data-ph-product="<?php echo esc_attr($atts['placeholder']); ?>"
data-ph-post="<?php echo esc_attr($atts['placeholder_post']); ?>"
>
<?php if ($trigger_type === 'icon') : ?>
<button type="button" class="mrbarati-coms-icon-trigger" aria-label="<?php echo esc_attr($atts['label']); ?>">
<span class="mrbarati-coms-icon-trigger-core">
<svg viewBox="0 0 24 24" aria-hidden="true">
<path d="M21 21l-4.35-4.35m1.35-5.65a7 7 0 11-14 0 7 7 0 0114 0z"/>
</svg>
</span>
</button>
<?php else : ?>
<button type="button" class="mrbarati-coms-button-trigger" aria-label="<?php echo esc_attr($atts['label']); ?>">
<span class="mrbarati-coms-button-icon">
<svg viewBox="0 0 24 24" aria-hidden="true">
<path d="M21 21l-4.35-4.35m1.35-5.65a7 7 0 11-14 0 7 7 0 0114 0z"/>
</svg>
</span>
<span class="mrbarati-coms-button-text"><?php echo esc_html($atts['label']); ?></span>
</button>
<?php endif; ?>
<div class="mrbarati-coms-overlay" aria-hidden="true">
<div class="mrbarati-coms-backdrop"></div>
<div class="mrbarati-coms-modal" role="dialog" aria-modal="true" aria-labelledby="<?php echo esc_attr($uid); ?>_title">
<button type="button" class="mrbarati-coms-close" aria-label="بستن">
<svg class="mrbarati-coms-close-x" viewBox="0 0 24 24" aria-hidden="true" focusable="false">
<path d="M6 6L18 18"/>
<path d="M18 6L6 18"/>
</svg>
</button>
<div class="mrbarati-coms-head">
<h3 id="<?php echo esc_attr($uid); ?>_title"><?php echo esc_html($atts['title']); ?></h3>
</div>
<div class="mrbarati-coms-switcher" role="tablist">
<button type="button" class="mrbarati-coms-tab is-active" data-type="product" role="tab" aria-selected="true">محصولات</button>
<button type="button" class="mrbarati-coms-tab" data-type="post" role="tab" aria-selected="false">نوشتهها</button>
</div>
<div class="mrbarati-coms-search-box">
<span class="mrbarati-coms-search-icon">
<svg viewBox="0 0 24 24" aria-hidden="true">
<path d="M21 21l-4.35-4.35m1.35-5.65a7 7 0 11-14 0 7 7 0 0114 0z"/>
</svg>
</span>
<input
type="search"
class="mrbarati-coms-input"
placeholder="<?php echo esc_attr($atts['placeholder']); ?>"
autocomplete="off"
inputmode="search"
/>
<button type="button" class="mrbarati-coms-submit">
<span>جستجو</span>
<svg viewBox="0 0 24 24" aria-hidden="true">
<path d="M5 12h14M13 5l7 7-7 7"/>
</svg>
</button>
</div>
<div class="mrbarati-coms-helper">حداقل ۳ کاراکتر وارد کن.</div>
<div class="mrbarati-coms-results-wrap">
<div class="mrbarati-coms-results"></div>
</div>
<div class="mrbarati-coms-credit">
طراح باکس جستجو : <a href="https://mrbarati.com/" target="_blank" rel="noopener">مهندس براتی</a>
</div>
</div>
</div>
</div>
<style>
#<?php echo esc_attr($uid); ?> {
--mrbarati-accent-1: #3c0e93;
--mrbarati-accent-2: #901eb1;
--mrbarati-danger: #e11d48;
--mrbarati-radius: 24px;
--mrbarati-border: rgba(15, 23, 42, .10);
--mrbarati-shadow: 0 20px 60px rgba(15, 23, 42, .22);
direction: rtl;
font-family: inherit;
color: inherit;
font-size: clamp(14px, 1vw + 12px, 16px);
}
body.mrbarati-coms-modal-open {
overflow: hidden;
}
#<?php echo esc_attr($uid); ?>,
#<?php echo esc_attr($uid); ?> * {
box-sizing: border-box;
font-family: inherit;
}
#<?php echo esc_attr($uid); ?> svg {
width: 20px;
height: 20px;
fill: none;
stroke: currentColor;
stroke-width: 2;
stroke-linecap: round;
stroke-linejoin: round;
}
#<?php echo esc_attr($uid); ?> .mrbarati-coms-icon-trigger,
#<?php echo esc_attr($uid); ?> .mrbarati-coms-button-trigger,
#<?php echo esc_attr($uid); ?> .mrbarati-coms-close,
#<?php echo esc_attr($uid); ?> .mrbarati-coms-tab,
#<?php echo esc_attr($uid); ?> .mrbarati-coms-submit {
font-family: inherit;
cursor: pointer;
-webkit-tap-highlight-color: transparent;
}
#<?php echo esc_attr($uid); ?> .mrbarati-coms-icon-trigger {
position: relative;
width: 52px;
height: 52px;
display: inline-flex;
align-items: center;
justify-content: center;
border: none;
border-radius: 999px;
background: linear-gradient(135deg, var(--mrbarati-accent-1), var(--mrbarati-accent-2));
color: #fff;
}
#<?php echo esc_attr($uid); ?> .mrbarati-coms-icon-trigger-core {
width: 34px;
height: 34px;
display: inline-flex;
align-items: center;
justify-content: center;
}
#<?php echo esc_attr($uid); ?> .mrbarati-coms-button-trigger {
position: relative;
display: inline-flex;
align-items: center;
justify-content: center;
gap: 10px;
min-height: 48px;
padding: 0 18px;
border: none;
border-radius: 999px;
background: linear-gradient(135deg, var(--mrbarati-accent-1), var(--mrbarati-accent-2));
color: #fff;
font-weight: 700;
font-size: 0.95em;
}
#<?php echo esc_attr($uid); ?> .mrbarati-coms-button-icon,
#<?php echo esc_attr($uid); ?> .mrbarati-coms-search-icon {
display: inline-flex;
align-items: center;
justify-content: center;
flex: 0 0 auto;
}
#<?php echo esc_attr($uid); ?> .mrbarati-coms-overlay {
position: fixed;
inset: 0;
z-index: 999999;
display: none;
place-items: center;
padding: 24px;
}
#<?php echo esc_attr($uid); ?>.is-open .mrbarati-coms-overlay {
display: grid;
}
#<?php echo esc_attr($uid); ?> .mrbarati-coms-backdrop {
position: absolute;
inset: 0;
background: rgba(2, 6, 23, .45);
backdrop-filter: blur(8px);
-webkit-backdrop-filter: blur(8px);
}
#<?php echo esc_attr($uid); ?> .mrbarati-coms-modal {
position: relative;
width: min(520px, 100%);
max-height: min(660px, calc(100vh - 34px));
overflow: hidden;
border-radius: var(--mrbarati-radius);
border: 1px solid var(--mrbarati-border);
background: #fff;
box-shadow: var(--mrbarati-shadow);
padding: clamp(18px, 3vw, 28px);
}
#<?php echo esc_attr($uid); ?> .mrbarati-coms-close {
position: absolute;
top: 14px;
left: 14px;
z-index: 4;
width: 36px;
height: 36px;
padding: 0;
border: none;
border-radius: 999px;
background: var(--mrbarati-danger);
color: #fff;
display: inline-flex;
align-items: center;
justify-content: center;
}
#<?php echo esc_attr($uid); ?> .mrbarati-coms-close .mrbarati-coms-close-x {
width: 18px;
height: 18px;
stroke: #fff;
stroke-width: 2.4;
fill: none;
}
#<?php echo esc_attr($uid); ?> .mrbarati-coms-head {
padding-left: 44px;
margin-bottom: 14px;
}
#<?php echo esc_attr($uid); ?> .mrbarati-coms-head h3 {
margin: 0;
color: #0f172a;
font-size: clamp(19px, 3vw, 26px);
font-weight: 800;
line-height: 1.25;
}
#<?php echo esc_attr($uid); ?> .mrbarati-coms-switcher {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 4px;
padding: 4px;
margin-bottom: 12px;
border-radius: 14px;
background: #f1f5f9;
border: 1px solid var(--mrbarati-border);
}
#<?php echo esc_attr($uid); ?> .mrbarati-coms-tab {
height: 38px;
border: 0;
border-radius: 10px;
background: transparent;
color: rgba(15,23,42,.62);
font-size: 0.85em;
font-weight: 700;
}
#<?php echo esc_attr($uid); ?> .mrbarati-coms-tab.is-active {
background: linear-gradient(135deg, var(--mrbarati-accent-1), var(--mrbarati-accent-2));
color: #fff;
}
#<?php echo esc_attr($uid); ?> .mrbarati-coms-search-box {
position: relative;
display: flex;
align-items: center;
gap: 8px;
min-height: 56px;
padding: 6px 8px;
border-radius: 18px;
background: #f8fafc;
border: 1px solid var(--mrbarati-border);
}
#<?php echo esc_attr($uid); ?> .mrbarati-coms-search-box:focus-within {
border-color: var(--mrbarati-accent-2);
}
#<?php echo esc_attr($uid); ?> .mrbarati-coms-search-icon {
width: 40px;
height: 40px;
border-radius: 12px;
color: var(--mrbarati-accent-2);
background: color-mix(in srgb, var(--mrbarati-accent-2) 10%, white 90%);
}
#<?php echo esc_attr($uid); ?> .mrbarati-coms-input {
flex: 1;
width: 100%;
min-width: 0;
height: 40px;
border: 0 !important;
outline: 0 !important;
background: transparent !important;
box-shadow: none !important;
color: #0f172a;
font-family: inherit;
font-size: 16px;
font-weight: 600;
}
#<?php echo esc_attr($uid); ?> .mrbarati-coms-input::placeholder {
color: rgba(15,23,42,.38);
font-weight: 500;
}
#<?php echo esc_attr($uid); ?> .mrbarati-coms-submit {
flex: 0 0 auto;
height: 40px;
display: inline-flex;
align-items: center;
justify-content: center;
gap: 6px;
padding: 0 14px;
border: 0;
border-radius: 12px;
background: linear-gradient(135deg, var(--mrbarati-accent-1), var(--mrbarati-accent-2));
color: #fff;
font-size: 0.85em;
font-weight: 700;
}
#<?php echo esc_attr($uid); ?> .mrbarati-coms-submit svg {
width: 15px;
height: 15px;
transform: rotate(180deg);
}
#<?php echo esc_attr($uid); ?> .mrbarati-coms-helper {
margin-top: 10px;
color: rgba(15,23,42,.52);
font-size: 0.8em;
font-weight: 600;
}
#<?php echo esc_attr($uid); ?> .mrbarati-coms-results-wrap {
margin-top: 14px;
max-height: min(380px, 42vh);
overflow: auto;
padding: 2px;
scrollbar-width: thin;
}
#<?php echo esc_attr($uid); ?> .mrbarati-coms-results {
display: grid;
gap: 8px;
}
#<?php echo esc_attr($uid); ?> .mrbarati-coms-result-card {
display: grid;
grid-template-columns: 60px 1fr;
align-items: center;
gap: 12px;
padding: 8px;
border-radius: 14px;
text-decoration: none !important;
background: #f8fafc;
border: 1px solid var(--mrbarati-border);
color: #0f172a;
}
#<?php echo esc_attr($uid); ?> .mrbarati-coms-thumb {
width: 60px;
height: 60px;
border-radius: 12px;
overflow: hidden;
background: rgba(15,23,42,.05);
}
#<?php echo esc_attr($uid); ?> .mrbarati-coms-thumb img {
width: 100%;
height: 100%;
display: block;
object-fit: cover;
}
#<?php echo esc_attr($uid); ?> .mrbarati-coms-info {
min-width: 0;
display: grid;
gap: 3px;
}
#<?php echo esc_attr($uid); ?> .mrbarati-coms-title {
color: #0f172a;
font-size: 0.9em;
font-weight: 800;
line-height: 1.5;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
#<?php echo esc_attr($uid); ?> .mrbarati-coms-price,
#<?php echo esc_attr($uid); ?> .mrbarati-coms-meta {
color: #0f172a;
font-size: 0.85em;
font-weight: 800;
}
#<?php echo esc_attr($uid); ?> .mrbarati-coms-meta {
color: rgba(15,23,42,.55);
font-weight: 600;
}
#<?php echo esc_attr($uid); ?> .mrbarati-coms-price del {
opacity: .48;
font-weight: 600;
}
#<?php echo esc_attr($uid); ?> .mrbarati-coms-price ins {
text-decoration: none;
}
#<?php echo esc_attr($uid); ?> .mrbarati-coms-empty,
#<?php echo esc_attr($uid); ?> .mrbarati-coms-error,
#<?php echo esc_attr($uid); ?> .mrbarati-coms-view-all {
padding: 16px;
border-radius: 16px;
background: #f8fafc;
border: 1px solid var(--mrbarati-border);
color: rgba(15,23,42,.68);
font-weight: 700;
font-size: 0.85em;
text-align: center;
}
#<?php echo esc_attr($uid); ?> .mrbarati-coms-view-all {
display: block;
text-decoration: none !important;
color: #fff;
background: linear-gradient(135deg, var(--mrbarati-accent-1), var(--mrbarati-accent-2));
margin-top: 8px;
}
#<?php echo esc_attr($uid); ?> .mrbarati-coms-credit {
margin-top: 14px;
text-align: center;
font-size: 11px;
color: rgba(15, 23, 42, .45);
}
#<?php echo esc_attr($uid); ?> .mrbarati-coms-credit a {
color: rgba(15, 23, 42, .55);
text-decoration: underline;
}
@media (max-width: 640px) {
#<?php echo esc_attr($uid); ?> .mrbarati-coms-icon-trigger {
width: 44px;
height: 44px;
}
#<?php echo esc_attr($uid); ?> .mrbarati-coms-icon-trigger-core {
width: 28px;
height: 28px;
}
#<?php echo esc_attr($uid); ?> .mrbarati-coms-button-trigger {
min-height: 44px;
max-width: 100%;
gap: 8px;
padding: 0 12px;
}
#<?php echo esc_attr($uid); ?> .mrbarati-coms-button-text {
max-width: min(48vw, 190px);
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
line-height: 1;
}
#<?php echo esc_attr($uid); ?> .mrbarati-coms-overlay {
padding: 20px 16px;
align-items: center;
}
#<?php echo esc_attr($uid); ?> .mrbarati-coms-modal {
width: 100%;
max-height: calc(100vh - 40px);
margin: 20px 0;
border-radius: var(--mrbarati-radius);
padding: 18px 14px 14px;
}
#<?php echo esc_attr($uid); ?> .mrbarati-coms-head {
padding-left: 40px;
}
#<?php echo esc_attr($uid); ?> .mrbarati-coms-search-box {
display: grid;
grid-template-columns: 40px 1fr;
min-height: auto;
padding: 8px;
}
#<?php echo esc_attr($uid); ?> .mrbarati-coms-submit {
grid-column: 1 / -1;
width: 100%;
}
#<?php echo esc_attr($uid); ?> .mrbarati-coms-results-wrap {
max-height: 46vh;
margin-top: 12px;
padding: 4px 6px;
}
}
</style>
<script>
(function () {
const root = document.getElementById('<?php echo esc_js($uid); ?>');
if (!root) return;
const trigger = root.querySelector('.mrbarati-coms-icon-trigger, .mrbarati-coms-button-trigger');
const overlay = root.querySelector('.mrbarati-coms-overlay');
const backdrop = root.querySelector('.mrbarati-coms-backdrop');
const closeBtn = root.querySelector('.mrbarati-coms-close');
const input = root.querySelector('.mrbarati-coms-input');
const submit = root.querySelector('.mrbarati-coms-submit');
const results = root.querySelector('.mrbarati-coms-results');
const helper = root.querySelector('.mrbarati-coms-helper');
const tabs = root.querySelectorAll('.mrbarati-coms-tab');
const ajaxUrl = root.dataset.ajax;
const nonce = root.dataset.nonce;
const limit = root.dataset.limit || 15;
const bases = {
product: root.dataset.searchProduct,
post: root.dataset.searchPost
};
const placeholders = {
product: root.dataset.phProduct,
post: root.dataset.phPost
};
let currentType = 'product';
let timer = null;
let controller = null;
let lastKey = '';
const escapeHTML = function (str) {
return String(str || '').replace(/[&<>"']/g, function (m) {
return {
'&': '&',
'<': '<',
'>': '>',
'"': '"',
"'": '''
}[m];
});
};
const openModal = function () {
root.classList.add('is-open');
overlay.setAttribute('aria-hidden', 'false');
document.body.classList.add('mrbarati-coms-modal-open');
input.focus();
};
const closeModal = function () {
root.classList.remove('is-open');
overlay.setAttribute('aria-hidden', 'true');
document.body.classList.remove('mrbarati-coms-modal-open');
};
const renderIdle = function () {
results.innerHTML = '';
helper.textContent = 'حداقل ۳ کاراکتر وارد کن.';
};
const renderEmpty = function (keyword) {
results.innerHTML = '<div class="mrbarati-coms-empty">برای «' + escapeHTML(keyword) + '» نتیجهای پیدا نشد.</div>';
};
const renderError = function () {
results.innerHTML = '<div class="mrbarati-coms-error">مشکلی در جستجو پیش آمد. دوباره امتحان کن.</div>';
};
const fullUrl = function (keyword) {
return bases[currentType].replace('__BLPS_QUERY__', encodeURIComponent(keyword));
};
const viewAllLink = function (keyword) {
return '<a class="mrbarati-coms-view-all" href="' + escapeHTML(fullUrl(keyword)) + '">مشاهده همه نتایج برای «' + escapeHTML(keyword) + '»</a>';
};
const renderResults = function (items, keyword) {
if (!items || !items.length) {
renderEmpty(keyword);
return;
}
const cards = items.map(function (item) {
const second = item.price
? '<span class="mrbarati-coms-price">' + item.price + '</span>'
: (item.meta ? '<span class="mrbarati-coms-meta">' + escapeHTML(item.meta) + '</span>' : '');
return '<a class="mrbarati-coms-result-card" href="' + escapeHTML(item.url) + '">' +
'<span class="mrbarati-coms-thumb">' +
(item.image ? '<img src="' + escapeHTML(item.image) + '" alt="' + escapeHTML(item.title) + '" loading="lazy">' : '') +
'</span>' +
'<span class="mrbarati-coms-info">' +
'<strong class="mrbarati-coms-title">' + escapeHTML(item.title) + '</strong>' +
second +
'</span>' +
'</a>';
}).join('');
results.innerHTML = cards + viewAllLink(keyword);
};
const doSearch = function () {
const keyword = input.value.trim();
const cacheKey = currentType + '|' + keyword;
if (keyword.length < 3) {
lastKey = '';
if (controller) controller.abort();
renderIdle();
return;
}
if (cacheKey === lastKey) return;
lastKey = cacheKey;
if (controller) controller.abort();
controller = new AbortController();
helper.textContent = 'در حال جستجو...';
results.innerHTML = '';
const formData = new FormData();
formData.append('action', 'blps_live_search');
formData.append('nonce', nonce);
formData.append('keyword', keyword);
formData.append('limit', limit);
formData.append('type', currentType);
fetch(ajaxUrl, {
method: 'POST',
credentials: 'same-origin',
body: formData,
signal: controller.signal
})
.then(function (r) { return r.json(); })
.then(function (data) {
helper.textContent = currentType === 'product' ? 'نتایج زنده محصولات' : 'نتایج زنده نوشتهها';
if (!data || !data.success) {
renderError();
return;
}
renderResults(data.data.items, keyword);
})
.catch(function (err) {
if (err.name === 'AbortError') return;
renderError();
});
};
const debouncedSearch = function () {
clearTimeout(timer);
timer = setTimeout(doSearch, 320);
};
const goToFullSearch = function () {
const keyword = input.value.trim();
if (keyword.length >= 3) {
window.location.href = fullUrl(keyword);
} else {
input.focus();
helper.textContent = 'برای جستجو حداقل ۳ کاراکتر وارد کن.';
}
};
tabs.forEach(function (tab) {
tab.addEventListener('click', function () {
if (tab.classList.contains('is-active')) return;
tabs.forEach(function (t) {
t.classList.remove('is-active');
t.setAttribute('aria-selected', 'false');
});
tab.classList.add('is-active');
tab.setAttribute('aria-selected', 'true');
currentType = tab.dataset.type === 'post' ? 'post' : 'product';
input.placeholder = placeholders[currentType] || '';
lastKey = '';
results.innerHTML = '';
if (input.value.trim().length >= 3) {
doSearch();
} else {
renderIdle();
}
input.focus();
});
});
trigger.addEventListener('click', openModal);
closeBtn.addEventListener('click', closeModal);
backdrop.addEventListener('click', closeModal);
submit.addEventListener('click', goToFullSearch);
input.addEventListener('input', debouncedSearch);
input.addEventListener('keydown', function (event) {
if (event.key === 'Enter') {
event.preventDefault();
goToFullSearch();
}
});
document.addEventListener('keydown', function (event) {
if (event.key === 'Escape' && root.classList.contains('is-open')) {
closeModal();
}
});
})();
</script>
<?php
return ob_get_clean();
}
add_action('wp_ajax_blps_live_search', 'blps_live_search_ajax');
add_action('wp_ajax_nopriv_blps_live_search', 'blps_live_search_ajax');
function blps_live_search_ajax() {
check_ajax_referer('blps_live_search', 'nonce');
$keyword = isset($_POST['keyword']) ? sanitize_text_field(wp_unslash($_POST['keyword'])) : '';
$limit = isset($_POST['limit']) ? absint($_POST['limit']) : 15;
$limit = max(3, min(20, $limit));
$type = (isset($_POST['type']) && $_POST['type'] === 'post') ? 'post' : 'product';
if ($type === 'product' && !class_exists('WooCommerce')) {
wp_send_json_error(['message' => 'WooCommerce is not active.']);
}
$keyword_length = function_exists('mb_strlen') ? mb_strlen($keyword) : strlen($keyword);
if ($keyword_length < 3) {
wp_send_json_success(['items' => []]);
}
/* ۱) جستجوی استاندارد وردپرس / ووکامرس */
$query_args = [
'post_type' => $type,
'post_status' => 'publish',
'posts_per_page' => $limit,
's' => $keyword,
'no_found_rows' => true,
'ignore_sticky_posts' => true,
'suppress_filters' => false,
'fields' => 'ids',
];
if ($type === 'product' && function_exists('wc_get_product_visibility_term_ids')) {
$visibility_terms = wc_get_product_visibility_term_ids();
if (!empty($visibility_terms['exclude-from-search'])) {
$query_args['tax_query'] = [
[
'taxonomy' => 'product_visibility',
'field' => 'term_taxonomy_id',
'terms' => [$visibility_terms['exclude-from-search']],
'operator' => 'NOT IN',
],
];
}
}
$ids = get_posts($query_args);
/* ۲) fallback با فیلتر posts_where (بدون کوئری خام) */
if (empty($ids)) {
$GLOBALS['blps_like_term'] = $keyword;
add_filter('posts_where', 'blps_posts_where_like', 10, 1);
$ids = get_posts([
'post_type' => $type,
'post_status' => 'publish',
'posts_per_page' => $limit,
'orderby' => 'date',
'order' => 'DESC',
'no_found_rows' => true,
'ignore_sticky_posts' => true,
'fields' => 'ids',
]);
remove_filter('posts_where', 'blps_posts_where_like', 10);
unset($GLOBALS['blps_like_term']);
}
$items = [];
foreach ((array) $ids as $post_id) {
$post_id = (int) $post_id;
$image = get_the_post_thumbnail_url($post_id, 'woocommerce_thumbnail');
if (!$image) {
$image = get_the_post_thumbnail_url($post_id, 'medium');
}
if (!$image && $type === 'product' && function_exists('wc_placeholder_img_src')) {
$image = wc_placeholder_img_src('woocommerce_thumbnail');
}
$row = [
'id' => $post_id,
'title' => wp_strip_all_tags(get_the_title($post_id)),
'url' => esc_url_raw(get_permalink($post_id)),
'image' => $image ? esc_url_raw($image) : '',
'price' => '',
'meta' => '',
];
if ($type === 'product') {
$product = function_exists('wc_get_product') ? wc_get_product($post_id) : null;
if (!$product) {
continue;
}
$row['price'] = wp_kses_post($product->get_price_html());
} else {
$row['meta'] = get_the_date('', $post_id);
}
$items[] = $row;
}
wp_send_json_success(['items' => $items]);
}
function blps_posts_where_like($where) {
global $wpdb;
$term = isset($GLOBALS['blps_like_term']) ? $GLOBALS['blps_like_term'] : '';
if ($term === '') {
return $where;
}
$like = '%' . $wpdb->esc_like($term) . '%';
$clause = $wpdb->prepare(
' AND (' . $wpdb->posts . '.post_title LIKE %s OR ' . $wpdb->posts . '.post_excerpt LIKE %s) ',
$like,
$like
);
return $where . $clause;
}
پیش نیاز قرار گیری کد افزونه دبلیو کد
کاهش مصرف منابع سرور
جستجوی محصولات تنها زمانی انجام میشود که کاربر حداقل سه کاراکتر وارد کند. همچنین سیستم جستجو دارای قابلیت Debounce بوده و با تأخیر کوتاهی درخواستها را ارسال میکند.
نویسنده و ایده پرداز کد: مهندس براتی مدرس و پشتیبان وردپرس
۲ پاسخ
کاش افزونه بود…
پلاگین هم داره اماده میشه…