jQuery والتعامل مع DOM

AJAX مع jQuery - الجزء الثاني

15 دقيقة الدرس 27 من 30

طرق AJAX المختصرة في jQuery

توفر jQuery طرقاً مختصرة ملائمة لعمليات AJAX الشائعة. تبسط هذه الطرق شيفرتك وتجعلها أكثر قابلية للقراءة، مع الاستمرار في توفير وظائف قوية.

الدالة $.get()

الدالة $.get() هي اختصار لإجراء طلبات GET:

البنية الأساسية:
$.get(url, data, callback, dataType);

// مثال
$.get('/api/users', { page: 1 }, function(data) {
    console.log('المستخدمون:', data);
}, 'json');
مثال عملي: تحميل المنتجات
<div id="products-container"></div>
<button id="load-products">تحميل المنتجات</button>

<script>
$('#load-products').click(function() {
    // طلب GET بسيط
    $.get('https://fakestoreapi.com/products', function(products) {
        var html = '';
        products.slice(0, 5).forEach(function(product) {
            html += '<div class="product-card">' +
                    '<h4>' + product.title + '</h4>' +
                    '<p>السعر: $' + product.price + '</p>' +
                    '<p>' + product.description.substring(0, 100) + '...</p>' +
                    '</div>';
        });
        $('#products-container').html(html);
    });
});
</script>

الدالة $.post()

الدالة $.post() هي اختصار لإجراء طلبات POST:

البنية الأساسية:
$.post(url, data, callback, dataType);

// مثال
$.post('/api/register', { username: 'john', email: 'john@example.com' }, function(response) {
    console.log('تم التسجيل:', response);
}, 'json');
مثال: إرسال بيانات النموذج
<form id="contact-form">
    <input type="text" name="name" placeholder="الاسم" required>
    <input type="email" name="email" placeholder="البريد الإلكتروني" required>
    <textarea name="message" placeholder="الرسالة" required></textarea>
    <button type="submit">إرسال الرسالة</button>
</form>
<div id="form-message"></div>

<script>
$('#contact-form').submit(function(e) {
    e.preventDefault();

    var formData = {
        name: $('[name="name"]').val(),
        email: $('[name="email"]').val(),
        message: $('[name="message"]').val()
    };

    $.post('/api/contact', formData)
        .done(function(response) {
            $('#form-message').html(
                '<div class="success">تم إرسال الرسالة بنجاح!</div>'
            );
            $('#contact-form')[0].reset();
        })
        .fail(function() {
            $('#form-message').html(
                '<div class="error">فشل إرسال الرسالة. يرجى المحاولة مرة أخرى.</div>'
            );
        });
});
</script>

الدالة $.getJSON()

الدالة $.getJSON() مصممة خصيصاً لجلب بيانات JSON:

مثال: تحميل بيانات الطقس
<div id="weather">
    <button id="get-weather">الحصول على الطقس</button>
    <div id="weather-info"></div>
</div>

<script>
$('#get-weather').click(function() {
    var city = 'London';
    var apiKey = 'your-api-key';
    var url = 'https://api.openweathermap.org/data/2.5/weather';

    $.getJSON(url, {
        q: city,
        appid: apiKey,
        units: 'metric'
    }, function(data) {
        var html = '<h3>' + data.name + '</h3>' +
                  '<p>درجة الحرارة: ' + data.main.temp + '°C</p>' +
                  '<p>الطقس: ' + data.weather[0].description + '</p>' +
                  '<p>الرطوبة: ' + data.main.humidity + '%</p>';
        $('#weather-info').html(html);
    });
});
</script>

الدالة $.load()

تقوم الدالة $.load() بتحميل HTML من الخادم وإدراجه مباشرة في العناصر المحددة:

مثال: تحميل محتوى جزئي
<div id="header"></div>
<div id="sidebar"></div>
<div id="content"></div>

<script>
// تحميل الملفات الكاملة
$('#header').load('/templates/header.html');
$('#sidebar').load('/templates/sidebar.html');

// تحميل جزء محدد باستخدام المحدد
$('#content').load('/page.html #main-content');

// تحميل مع دالة استدعاء
$('#content').load('/article.html', function(response, status, xhr) {
    if (status === 'error') {
        $(this).html('<p>عذراً، لا يمكن تحميل المحتوى.</p>');
    } else {
        console.log('تم تحميل المحتوى بنجاح');
    }
});

// تحميل مع معاملات
$('#results').load('/search.php', { query: 'jquery', limit: 10 });
</script>
نصيحة: الدالة $.load() فريدة من نوعها لأنها تُستدعى على تحديد jQuery، وليس على $ مباشرة. تقوم تلقائياً بإدراج الاستجابة في العناصر المطابقة.

واجهة الوعود مع .done() و .fail() و .always()

جميع دوال AJAX في jQuery تُرجع كائن jqXHR الذي ينفذ واجهة الوعود:

مثال: استخدام دوال الوعود
// سلسلة وعود أساسية
$.get('/api/user/1')
    .done(function(data) {
        console.log('نجح:', data);
    })
    .fail(function(xhr, status, error) {
        console.log('خطأ:', error);
    })
    .always(function() {
        console.log('اكتمل الطلب');
    });

// معالجات متعددة
var request = $.getJSON('/api/products');

request.done(function(products) {
    console.log('تم تحميل', products.length, 'منتجات');
});

request.done(function(products) {
    $('#product-count').text(products.length);
});

request.fail(function() {
    $('#error-message').show();
});

request.always(function() {
    $('#loader').hide();
});

ربط طلبات AJAX المتعددة

مثال: الطلبات المتسلسلة
// تحميل المستخدم، ثم تحميل منشوراته، ثم تحميل التعليقات
$.get('/api/user/1')
    .done(function(user) {
        console.log('المستخدم:', user.name);
        $('#user-name').text(user.name);

        return $.get('/api/user/' + user.id + '/posts');
    })
    .done(function(posts) {
        console.log('المنشورات:', posts.length);
        $('#post-count').text(posts.length);

        return $.get('/api/posts/' + posts[0].id + '/comments');
    })
    .done(function(comments) {
        console.log('التعليقات:', comments.length);
        $('#comment-count').text(comments.length);
    })
    .fail(function(error) {
        console.log('خطأ في السلسلة:', error);
    });

طلبات AJAX المتوازية مع $.when()

استخدم $.when() لتنفيذ الشيفرة بعد اكتمال طلبات AJAX المتعددة:

مثال: تحميل موارد متعددة
// تنفيذ طلبات متعددة بشكل متوازٍ
var usersRequest = $.get('/api/users');
var productsRequest = $.get('/api/products');
var categoriesRequest = $.get('/api/categories');

$.when(usersRequest, productsRequest, categoriesRequest)
    .done(function(usersData, productsData, categoriesData) {
        // جميع الطلبات الثلاثة اكتملت بنجاح
        var users = usersData[0];      // العنصر الأول هو البيانات
        var products = productsData[0];
        var categories = categoriesData[0];

        console.log('المستخدمون:', users.length);
        console.log('المنتجات:', products.length);
        console.log('الفئات:', categories.length);

        // تهيئة لوحة المعلومات بجميع البيانات
        initDashboard(users, products, categories);
    })
    .fail(function() {
        console.log('فشل طلب واحد أو أكثر');
        $('#error-message').text('فشل تحميل بيانات لوحة المعلومات');
    })
    .always(function() {
        $('#loader').hide();
    });

function initDashboard(users, products, categories) {
    $('#total-users').text(users.length);
    $('#total-products').text(products.length);
    $('#total-categories').text(categories.length);
}

معالجات أحداث AJAX العامة

توفر jQuery أحداث AJAX عامة تُطلق لجميع طلبات AJAX:

مثال: مؤشر تحميل عام
<div id="global-loader" style="display: none;">جاري التحميل...</div>

<script>
// إظهار المحمل عند بدء أي طلب AJAX
$(document).ajaxStart(function() {
    $('#global-loader').fadeIn();
});

// إخفاء المحمل عند اكتمال جميع طلبات AJAX
$(document).ajaxStop(function() {
    $('#global-loader').fadeOut();
});

// تتبع نشاط AJAX
var activeRequests = 0;

$(document).ajaxSend(function(event, xhr, settings) {
    activeRequests++;
    console.log('بدأ الطلب:', settings.url);
    console.log('الطلبات النشطة:', activeRequests);
});

$(document).ajaxComplete(function(event, xhr, settings) {
    activeRequests--;
    console.log('اكتمل الطلب:', settings.url);
    console.log('الطلبات النشطة:', activeRequests);
});

// معالج خطأ عام
$(document).ajaxError(function(event, xhr, settings, error) {
    console.error('خطأ AJAX:', settings.url, error);
    if (xhr.status === 401) {
        window.location.href = '/login';
    }
});

// معالج نجاح عام
$(document).ajaxSuccess(function(event, xhr, settings) {
    console.log('نجاح AJAX:', settings.url);
});
</script>

التخزين المؤقت لـ AJAX

مثال: تطبيق التخزين المؤقت لاستدعاءات API
var cache = {};

function getCachedData(url, forceRefresh) {
    // التحقق من التخزين المؤقت وعدم إجبار التحديث
    if (cache[url] && !forceRefresh) {
        console.log('إرجاع البيانات المخزنة مؤقتاً لـ:', url);
        return $.Deferred().resolve(cache[url]).promise();
    }

    // جلب من الخادم
    console.log('جلب من الخادم:', url);
    return $.get(url).done(function(data) {
        cache[url] = data;
    });
}

// الاستخدام
$('#load-users').click(function() {
    getCachedData('/api/users')
        .done(function(users) {
            displayUsers(users);
        });
});

$('#refresh-users').click(function() {
    getCachedData('/api/users', true)  // إجبار التحديث
        .done(function(users) {
            displayUsers(users);
        });
});

// التخزين المؤقت مع انتهاء الصلاحية
var cacheWithExpiry = {};

function getCachedDataWithExpiry(url, ttl) {
    var now = Date.now();
    var cached = cacheWithExpiry[url];

    // التحقق من التخزين المؤقت وعدم انتهاء الصلاحية
    if (cached && (now - cached.timestamp < ttl)) {
        console.log('إرجاع البيانات المخزنة مؤقتاً');
        return $.Deferred().resolve(cached.data).promise();
    }

    // جلب من الخادم
    return $.get(url).done(function(data) {
        cacheWithExpiry[url] = {
            data: data,
            timestamp: now
        };
    });
}

// التخزين المؤقت لمدة 5 دقائق (300000 ميلي ثانية)
getCachedDataWithExpiry('/api/products', 300000)
    .done(function(products) {
        displayProducts(products);
    });

إلغاء الطلب

مثال: إلغاء الطلبات الجارية
var currentRequest = null;

$('#search-input').on('input', function() {
    var query = $(this).val();

    // إلغاء الطلب السابق إذا كان لا يزال قيد التشغيل
    if (currentRequest) {
        currentRequest.abort();
        console.log('تم إلغاء الطلب السابق');
    }

    // عدم البحث إذا كان الاستعلام قصيراً جداً
    if (query.length < 3) {
        $('#search-results').empty();
        return;
    }

    // بدء طلب جديد
    currentRequest = $.get('/api/search', { q: query })
        .done(function(results) {
            displaySearchResults(results);
        })
        .fail(function(xhr) {
            if (xhr.statusText === 'abort') {
                console.log('تم إلغاء الطلب');
            } else {
                console.log('فشل البحث');
            }
        })
        .always(function() {
            currentRequest = null;
        });
});

function displaySearchResults(results) {
    var html = '';
    results.forEach(function(result) {
        html += '<div class="result-item">' + result.title + '</div>';
    });
    $('#search-results').html(html);
}

إعدادات AJAX الافتراضية

مثال: تكوين الإعدادات الافتراضية العامة
// تعيين الخيارات الافتراضية لجميع طلبات AJAX
$.ajaxSetup({
    timeout: 10000,
    cache: false,
    headers: {
        'X-CSRF-Token': $('meta[name="csrf-token"]').attr('content')
    },
    beforeSend: function(xhr) {
        var token = localStorage.getItem('auth_token');
        if (token) {
            xhr.setRequestHeader('Authorization', 'Bearer ' + token);
        }
    },
    error: function(xhr) {
        if (xhr.status === 401) {
            console.log('غير مصرح - إعادة التوجيه إلى تسجيل الدخول');
            window.location.href = '/login';
        }
    }
});

// جميع استدعاءات AJAX اللاحقة ستستخدم هذه الإعدادات الافتراضية
$.get('/api/protected-resource')
    .done(function(data) {
        console.log('البيانات:', data);
    });
تحذير: استخدم $.ajaxSetup() بحذر. تؤثر الإعدادات الافتراضية العامة على جميع طلبات AJAX في تطبيقك. غالباً ما يكون من الأفضل إنشاء دوال غلاف لحالات استخدام محددة.

العمل مع أنواع محتوى مختلفة

مثال: إرسال تنسيقات بيانات مختلفة
// إرسال JSON
$.ajax({
    url: '/api/data',
    method: 'POST',
    contentType: 'application/json',
    data: JSON.stringify({ name: 'John', age: 30 }),
    success: function(response) {
        console.log('تم إرسال JSON بنجاح');
    }
});

// إرسال بيانات مشفرة بالنموذج (افتراضي)
$.ajax({
    url: '/api/data',
    method: 'POST',
    data: { name: 'John', age: 30 },
    success: function(response) {
        console.log('تم إرسال بيانات النموذج بنجاح');
    }
});

// إرسال FormData (لتحميل الملفات)
var formData = new FormData();
formData.append('name', 'John');
formData.append('file', $('#file-input')[0].files[0]);

$.ajax({
    url: '/api/upload',
    method: 'POST',
    data: formData,
    processData: false,
    contentType: false,
    success: function(response) {
        console.log('تم تحميل الملف بنجاح');
    }
});
تمرين عملي:

أنشئ لوحة معلومات لتحميل البيانات متعددة الخطوات:

  1. أنشئ ثلاثة طلبات متوازية لتحميل المستخدمين والمنشورات والتعليقات باستخدام $.when()
  2. اعرض مؤشر تحميل عام أثناء تقدم الطلبات
  3. أظهر الإحصائيات: إجمالي المستخدمين والمنشورات والتعليقات
  4. نفّذ التخزين المؤقت مع انتهاء صلاحية لمدة دقيقتين
  5. أضف زر "تحديث" لفرض إعادة التحميل من الخادم
  6. اعرض المستخدم الأكثر نشاطاً (المستخدم الذي لديه أكثر المنشورات)
  7. أظهر المنشورات الحديثة مع عدد التعليقات
  8. عالج الأخطاء بشكل صحيح وأظهر رسائل مناسبة

نقاط نهاية API:

  • المستخدمون: https://jsonplaceholder.typicode.com/users
  • المنشورات: https://jsonplaceholder.typicode.com/posts
  • التعليقات: https://jsonplaceholder.typicode.com/comments

الخلاصة

في هذا الدرس، تعلمت:

  • طرق AJAX المختصرة في jQuery: $.get()، $.post()، $.getJSON()، $.load()
  • استخدام واجهة الوعود مع .done() و.fail() و.always()
  • ربط طلبات AJAX المتسلسلة
  • إجراء طلبات متوازية مع $.when()
  • معالجات أحداث AJAX العامة لتتبع جميع الطلبات
  • تطبيق استراتيجيات التخزين المؤقت
  • إلغاء الطلبات الجارية
  • تعيين إعدادات AJAX الافتراضية العامة مع $.ajaxSetup()
  • العمل مع أنواع محتوى مختلفة

بعد ذلك، سنستكشف العمل مع النماذج والتسلسل لجمع وإرسال بيانات النموذج بكفاءة عبر AJAX.