Use jQuery, dexie, indexdb, xpath, fetch with extreme compatibility with whole code.

Use the full potential of jQuery, dexie, indexdb, intersection observer, fetch, etc.

Utilise full capabilities of jQuery and dexie and index db and intersection observer and aos where ever possible in the whole code.

Improve it further.

Make it more better and robust.

Do whatever you want but just make it work.

Don't use ajax or anything that need a server or backend to work.

Client side solution only.

Send the fully working full codes.

<!-- JavaScript -->
  <script>
    'use strict';
    (() => {
      /**
       * Initialize Dexie Database with Unique Constraints on name, coverUrl, and pdfUrl
       */
      const db = new Dexie('MonochromeNovelsDB');
      db.version(1).stores({
        novels: '++id, &name, &coverUrl, &pdfUrl, timestamp' // Unique indexes on name, coverUrl, pdfUrl
      });

      // Constants
      const PAGE_SIZE = 20; // Number of novels to load per page
      let currentPage = 0; // Current page index
      let isLoading = false; // Flag to prevent multiple simultaneous loads
      let hasMore = true; // Flag to determine if more novels are available
      let currentSearchTerm = ''; // Current search term

      const PLACEHOLDER_IMAGE = 'https://via.placeholder.com/500x700?text=No+Image'; // Placeholder for missing images

      /**
       * Debounce Function to Limit Function Execution Rate
       * @param {Function} func
       * @param {number} delay
       * @returns {Function}
       */
      const debounce = (func, delay) => {
        let debounceTimer;
        return function(...args) {
          const context = this;
          clearTimeout(debounceTimer);
          debounceTimer = setTimeout(() => func.apply(context, args), delay);
        }
      };

      /**
       * Utility: Delay Function
       * @param {number} ms
       * @returns {Promise}
       */
      const delay = ms => new Promise(resolve => setTimeout(resolve, ms));

      /**
       * Utility: Retry Operation with Exponential Backoff (3 Attempts)
       * @param {Function} operation
       * @param {number} attempts
       * @param {number} delayDuration
       * @returns {Promise}
       */
      const retryOperation = async (operation, attempts = 3, delayDuration = 1500) => {
        for (let i = 0; i < attempts; i++) {
          try {
            return await operation();
          } catch (error) {
            if (i === attempts - 1) throw error;
            await delay(delayDuration * Math.pow(2, i)); // Exponential backoff
          }
        }
      };

      /**
       * Initialize Intersection Observer for Infinite Scroll using Sentinel Element
       */
      const setupSentinelObserver = () => {
        const sentinel = document.getElementById('sentinel');
        const sentinelObserver = new IntersectionObserver((entries) => {
          entries.forEach(entry => {
            if (entry.isIntersecting && hasMore && !isLoading) {
              loadNextPage();
            }
          });
        }, {
          rootMargin: '100px',
          threshold: 0
        });
        sentinelObserver.observe(sentinel);
      };

      /**
       * Load Next Page of Novels from IndexedDB
       */
      const loadNextPage = async () => {
        if (isLoading) return; // Prevent multiple simultaneous loads
        isLoading = true;
        showLoader();

        try {
          let novels;

          if (currentSearchTerm === '') {
            novels = await db.novels
              .orderBy('name') // Order by name for alphabetical display
              .offset(currentPage * PAGE_SIZE)
              .limit(PAGE_SIZE)
              .toArray();
          } else {
            novels = await db.novels
              .where('name')
              .startsWithIgnoreCase(currentSearchTerm)
              .offset(currentPage * PAGE_SIZE)
              .limit(PAGE_SIZE)
              .toArray();
          }

          if (novels.length < PAGE_SIZE) {
            hasMore = false; // No more novels to load
          }

          if (novels.length > 0) {
            appendNovelsToDisplay(novels);
            currentPage++;
          } else {
            if (currentPage === 0) {
              showError('No novels found for the given search term.');
            }
          }
        } catch (error) {
          console.error('Error loading novels:', error);
          showError('An error occurred while loading novels.');
        } finally {
          hideLoader();
          isLoading = false;
        }
      };

      /**
       * Show Loader
       */
      const showLoader = () => {
        const loader = document.getElementById('loader');
        if (loader) loader.classList.add('visible');
      };

      /**
       * Hide Loader
       */
      const hideLoader = () => {
        const loader = document.getElementById('loader');
        if (loader) loader.classList.remove('visible');
      };

      /**
       * Display Error Message
       * @param {string} message
       */
      const showError = message => {
        const content = document.getElementById('content');
        // Remove existing error messages
        const existingErrors = content.querySelectorAll('.error-message');
        existingErrors.forEach(error => error.remove());

        const errorDiv = document.createElement('div');
        errorDiv.className = 'error-message';
        errorDiv.setAttribute('role', 'alert');
        errorDiv.textContent = message;
        content.appendChild(errorDiv);
        hideLoader();
      };

      /**
       * Append Novels to Display
       * @param {Array} novels
       */
      const appendNovelsToDisplay = novels => {
        const content = document.getElementById('content');
        const fragment = document.createDocumentFragment();

        novels.forEach(novel => {
          const card = createNovelCard(novel);
          fragment.appendChild(card);
        });

        content.appendChild(fragment);
        AOS.refresh(); // Refresh AOS to apply animations to new elements
      };

      /**
       * Create Novel Card Element
       * @param {Object} novel
       * @returns {HTMLElement}
       */
      const createNovelCard = (novel) => {
        const card = document.createElement('div');
        card.className = 'card';
        card.setAttribute('tabindex', '0');
        card.setAttribute('role', 'button');
        card.setAttribute('aria-labelledby', novel-title-${encodeURIComponent(novel.name)});
        card.setAttribute('data-aos', 'fade-up'); // AOS attribute for animation

        // Construct HTML Structure: Image on top, title below
        card.innerHTML = `
          <div class="card__image-container">
            <img
              class="card__image"
              src="${escapeHTML(novel.coverUrl || PLACEHOLDER_IMAGE)}"
              alt="Cover of ${escapeHTML(novel.name)}"
              onerror="this.onerror=null;this.src='${PLACEHOLDER_IMAGE}';"
            />
          </div>
          <div class="card__content">
            <h2 id="novel-title-${encodeURIComponent(novel.name)}" class="card__title">${escapeHTML(novel.name)}</h2>
          </div>
        `;

        // Event Listener to Open Modal
        card.addEventListener('click', () => {
          showModal(novel);
        });

        // Keyboard Accessibility: Open Modal on Enter or Space Key
        card.addEventListener('keydown', event => {
          if (event.key === 'Enter' || event.key === ' ') {
            event.preventDefault();
            showModal(novel);
          }
        });

        return card;
      };

      /**
       * Utility: Escape HTML to Prevent XSS
       * @param {string} str
       * @returns {string}
       */
      const escapeHTML = (str) => {
        if (!str) return '';
        return str.replace(/&/g, "&amp;")
                  .replace(/</g, "&lt;")
                  .replace(/>/g, "&gt;")
                  .replace(/"/g, "&quot;")
                  .replace(/'/g, "&#039;");
      };

      /**
       * Modal Functionality
       */
      const modal = document.getElementById('modal');
      const modalTitle = document.getElementById('modal-title');
      const modalLink = document.getElementById('modal-link');
      const closeBtn = document.querySelector('.close');

      /**
       * Show Modal with Novel Details
       * @param {Object} novel
       */
      const showModal = (novel) => {
        modalTitle.textContent = novel.name;
        modalLink.href = novel.pdfUrl;
        modal.style.display = 'flex';
        modal.setAttribute('aria-hidden', 'false');
        document.body.style.overflow = 'hidden'; // Prevent background scrolling
        closeBtn.focus(); // Focus on close button for accessibility
      };

      /**
       * Close Modal
       */
      const closeModal = () => {
        modal.style.display = 'none';
        modal.setAttribute('aria-hidden', 'true');
        document.body.style.overflow = 'auto';
      };

      closeBtn.addEventListener('click', closeModal);

      window.addEventListener('click', event => {
        if (event.target === modal) {
          closeModal();
        }
      });

      window.addEventListener('keydown', event => {
        if (event.key === 'Escape' && modal.style.display === 'flex') {
          closeModal();
        }
      });

      /**
       * Search Functionality
       */
      const searchInput = document.getElementById('search-input');

      const handleSearch = debounce(async () => {
        currentSearchTerm = searchInput.value.trim();
        currentPage = 0;
        hasMore = true;

        const content = document.getElementById('content');
        content.innerHTML = ''; // Clear existing content
        const existingErrors = content.querySelectorAll('.error-message');
        existingErrors.forEach(error => error.remove());

        // Reload the first page with the new search term
        await loadNextPage();
      }, 300); // 300ms debounce delay

      searchInput.addEventListener('input', handleSearch);

      /**
       * Refresh Data Functionality
       */
      const refreshButton = document.getElementById('refresh-button');
      refreshButton.addEventListener('click', async () => {
        const confirmation = confirm('Are you sure you want to refresh the data? This may take a few moments.');
        if (!confirmation) return;

        isLoading = true;
        showLoader();
        hasMore = true;
        currentPage = 0;

        try {
          // Clear existing data
          await db.novels.clear();

          // Fetch and parse fresh data
          await fetchAndStoreNovels();

          // Reload the first page of novels
          const content = document.getElementById('content');
          content.innerHTML = ''; // Clear existing content
          const existingErrors = content.querySelectorAll('.error-message');
          existingErrors.forEach(error => error.remove());

          await loadNextPage();
        } catch (error) {
          console.error('Error refreshing data:', error);
          showError('Failed to refresh data. Please try again later.');
        } finally {
          hideLoader();
          isLoading = false;
        }
      });

      /**
       * Initialize Application
       */
      const initializeApp = async () => {
        try {
          // Check if any novels are already stored in IndexedDB
          const count = await db.novels.count();
          if (count === 0) {
            // If not, fetch from the target website
            await fetchAndStoreNovels();
          }

          // Load the first page of novels
          await loadNextPage();
        } catch (error) {
          console.error('Error initializing application:', error);
          showError('Failed to load novels. Please try refreshing the page.');
        }
      };

      /**
       * Fetch and Store Novels
       */
      const fetchAndStoreNovels = async () => {
        const html = await retryOperation(() => fetchHTML('https://jnovels.com/top-light-novels-to-read/'));
        let novels = parseHTML(html);

        if (novels.length === 0) {
          throw new Error('No novels found on the target page.');
        }

        // Bulk upsert novels to IndexedDB
        try {
          await db.novels.bulkPut(novels);
        } catch (bulkPutError) {
          if (bulkPutError.name === 'BulkError') {
            // Ignore duplicate entries and continue
            console.warn('Some novels were duplicates and were skipped.');
          } else {
            console.error('Error bulk upserting novels:', bulkPutError);
            throw bulkPutError; // Re-throw to be handled by caller
          }
        }
      };

      /**
       * Fetch HTML Content with Content-Type Validation via CORS Proxy
       * @param {string} url
       * @returns {Promise<string>}
       */
      const fetchHTML = async (url) => {
        // Using allorigins API to bypass CORS
        const proxyUrl = https://api.allorigins.win/raw?url=${encodeURIComponent(url)};
        const response = await fetch(proxyUrl);
        if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`);

        // Check Content-Type Header
        const contentType = response.headers.get('Content-Type');
        if (!contentType || !contentType.includes('text/html')) {
          throw new Error('Fetched content is not of type text/html');
        }

        return await response.text();
      };

      /**
       * Parse Fetched HTML to Extract Novel Data Based on Provided Schema
       * @param {string} html
       * @returns {Array<Object>}
       */
      const parseHTML = (html) => {
        const parser = new DOMParser();
        const doc = parser.parseFromString(html, 'text/html');
        const novels = [];
        const uniqueNovels = new Set();

        // XPath selectors
        const xpathBookName = '//h3/a/span[@style="color: #ff6600;"]';
        const xpathPdfLink = '//h3/a/@href';
        const xpathCoverUrl = '//h3/following-sibling::p[1]/a/img/@src';

        const bookNameNodes = doc.evaluate(xpathBookName, doc, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
        const pdfLinkNodes = doc.evaluate(xpathPdfLink, doc, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
        const coverUrlNodes = doc.evaluate(xpathCoverUrl, doc, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);

        for (let i = 0; i < bookNameNodes.snapshotLength; i++) {
          try {
            const name = bookNameNodes.snapshotItem(i).textContent.trim();
            const pdfLink = pdfLinkNodes.snapshotItem(i).nodeValue.trim();
            let coverUrl = coverUrlNodes.snapshotItem(i).nodeValue.trim();

            // Handle different URL formats
            if (coverUrl) {
              if (coverUrl.startsWith('//')) {
                coverUrl = https:${coverUrl};
              } else if (coverUrl.startsWith('/')) {
                coverUrl = https://jnovels.com${coverUrl};
              } else if (!coverUrl.startsWith('http')) {
                // Handle relative URLs without leading '/'
                coverUrl = https://jnovels.com/${coverUrl};
              }
            }

            // Validate extracted data
            if (name && pdfLink && coverUrl) {
              const novelKey = ${name}-${coverUrl}-${pdfLink};
              if (!uniqueNovels.has(novelKey)) {
                uniqueNovels.add(novelKey);
                novels.push({
                  name,
                  coverUrl,
                  pdfUrl: pdfLink,
                  timestamp: Date.now()
                });
              }
            } else {
              console.warn('Incomplete novel data found and skipped:', { name, pdfLink, coverUrl });
            }
          } catch (error) {
            // Skip any malformed entries
            console.error('Error parsing a novel entry:', error);
          }
        }

        return novels;
      };

      /**
       * Initialize AOS and Start the Application once the DOM is fully loaded
       */
      document.addEventListener('DOMContentLoaded', () => {
        // Initialize AOS
        AOS.init({
          duration: 800,
          easing: 'ease-in-out',
          once: true,
        });
        setupSentinelObserver();
        initializeApp();
      });
    })();