Browse our ready-to-use printables, templates and e‑books — available for instant download.
Trusted by 200+ event planners & entrepreneurs
Could not load product list.
'; sidebarWrapper.style.display = 'none'; loadMoreButton.style.display = 'none'; return; } Array.from(productListUL.querySelectorAll('li.product')).forEach(productEl => { const link = productEl.querySelector('a.woocommerce-LoopProduct-link'); if (link) { const contentWrapper = document.createElement('div'); contentWrapper.className = 'product-card-content'; const title = productEl.querySelector('.woocommerce-loop-product__title'); const price = productEl.querySelector('.price'); const rating = productEl.querySelector('.star-rating'); const button = productEl.querySelector('.button'); if (title) contentWrapper.appendChild(title); if (rating) contentWrapper.appendChild(rating); if (price) contentWrapper.appendChild(price); if (button) contentWrapper.appendChild(button); productEl.appendChild(contentWrapper); } }); let allProductsData = []; const processProductData = (productEl, index) => { const titleEl = productEl.querySelector('.woocommerce-loop-product__title'); const priceRegularEl = productEl.querySelector('.price > .woocommerce-Price-amount.amount > bdi'); const priceSaleEl = productEl.querySelector('.price > ins > .woocommerce-Price-amount.amount > bdi'); const priceSimpleEl = productEl.querySelector('.price > .amount > bdi'); let priceTextContent = null; if (priceSaleEl) { priceTextContent = priceSaleEl.textContent; } else if (priceRegularEl) { priceTextContent = priceRegularEl.textContent; } else if (priceSimpleEl) { priceTextContent = priceSimpleEl.textContent; } let priceMeta = productEl.querySelector('meta[itemprop="price"]'); let priceValue = NaN; if (priceMeta && priceMeta.content) { priceValue = parseFloat(priceMeta.content); } else if (priceTextContent) { let rawPrice = priceTextContent.replace(/[^0-9.,]/g, ''); if (rawPrice.includes(',') && rawPrice.includes('.')) { rawPrice = rawPrice.replace(/\./g, '').replace(',', '.');} else { rawPrice = rawPrice.replace(/,/g, '.'); } priceValue = parseFloat(rawPrice); } return { element: productEl, id: productEl.classList.contains('postid') ? (productEl.className.match(/post-(\d+)/) ? productEl.className.match(/post-(\d+)/)[1] : Math.random().toString()) : Math.random().toString(), title: titleEl ? titleEl.textContent.trim().toLowerCase() : '', price: isNaN(priceValue) ? Infinity : priceValue, categories: Array.from(productEl.classList).filter(c => c.startsWith('product_cat-')).map(c => c.substring('product_cat-'.length)), tags: Array.from(productEl.classList).filter(c => c.startsWith('product_tag-')).map(c => c.substring('product_tag-'.length)), isOnSale: productEl.querySelector('.onsale') !== null, isInStock: !productEl.classList.contains('outofstock'), isFeatured: productEl.classList.contains('featured'), originalIndex: index }; }; allProductsData = Array.from(productListUL.querySelectorAll('li.product')).map(processProductData); if (allProductsData.length === 0) { sidebarWrapper.style.display = 'none'; productListContainerEl.innerHTML = 'No products available to display.
'; loadMoreButton.style.display = 'none'; return; } let currentFilters = { categories: [], tag: 'all', searchTerm: '', minPrice: null, maxPrice: null, sale: 'any', stock: 'any', sortBy: 'popularity' }; let noProductsMessageEl = null; // --- Load More Logic --- const PRODUCTS_PER_PAGE = 9; // Show 9 products initially (3x3) let numProductsToShow = PRODUCTS_PER_PAGE; let currentlyDisplayedProducts = []; // Array de los productos actualmente en el DOM loadMoreButton.addEventListener('click', () => { numProductsToShow += PRODUCTS_PER_PAGE; renderProductList(currentlyDisplayedProducts); // Re-render con más productos }); // --- End Load More Logic --- const allPrices = allProductsData.map(p => p.price).filter(p => p !== Infinity && p > 0); const globalMinPrice = allPrices.length > 0 ? Math.floor(Math.min(...allPrices)) : 0; const globalMaxPrice = allPrices.length > 0 ? Math.ceil(Math.max(...allPrices)) : 1000; minPriceInput.placeholder = `Min (${globalMinPrice})`; maxPriceInput.placeholder = `Max (${globalMaxPrice})`; priceRangeValuesEl.textContent = `Range: ${globalMinPrice} - ${globalMaxPrice}`; const DEFAULT_COLUMNS = '3'; const applyColumnSetting = (numCols) => { productListUL.className = productListUL.className.replace(/\bcolumns-\d+\b/g, '').trim(); productListUL.classList.add(`columns-${numCols}`); if (!productListUL.classList.contains('woocommerce')) productListUL.classList.add('woocommerce'); localStorage.setItem('productFilterColumns', numCols); if (columnSelector.value !== numCols) columnSelector.value = numCols; }; columnSelector.value = localStorage.getItem('productFilterColumns') || DEFAULT_COLUMNS; applyColumnSetting(columnSelector.value); columnSelector.addEventListener('change', (e) => { applyColumnSetting(e.target.value); // Cuando cambian las columnas, el número de items por "página" podría ser relevante. // Por ahora, mantenemos el PRODUCTS_PER_PAGE fijo. // Si el número de columnas cambia, puede que queramos resetear numProductsToShow al cambiar filtros. }); const getUniqueTerms = (productDataList, termTypeKey) => { const terms = new Map(); productDataList.forEach(product => { product[termTypeKey].forEach(slug => { if (!terms.has(slug)) { const name = slug.replace(/-/g, ' ').replace(/\b\w/g, l => l.toUpperCase()); terms.set(slug, name); } }); }); return Array.from(terms, ([slug, name]) => ({ slug, name })).sort((a, b) => a.name.localeCompare(b.name)); }; const updateCategoryCountsAndRender = () => { const productsForCount = allProductsData.filter(p => { const tagMatch = currentFilters.tag === 'all' || p.tags.includes(currentFilters.tag); const searchMatch = currentFilters.searchTerm === '' || p.title.includes(currentFilters.searchTerm); const minPriceMatch = currentFilters.minPrice === null || p.price >= currentFilters.minPrice; const maxPriceMatch = currentFilters.maxPrice === null || p.price { p.categories.forEach(catSlug => { categoryCounts[catSlug] = (categoryCounts[catSlug] || 0) + 1; }); }); const categoriesData = getUniqueTerms(allProductsData, 'categories'); createCategoryCheckboxes(categoryCheckboxesContainer, categoriesData, categoryCounts); }; const createCategoryCheckboxes = (container, categoriesData, counts = {}) => { if (!container) return; container.innerHTML = ''; categoriesData.forEach(cat => { const count = counts[cat.slug] || 0; const listItem = document.createElement('li'); const isChecked = currentFilters.categories.includes(cat.slug); const isDisabled = count === 0 && !isChecked; listItem.innerHTML = ` `; container.appendChild(listItem); }); }; categoryCheckboxesContainer.addEventListener('change', (event) => { if (event.target.type === 'checkbox' && event.target.name === 'product_cat') { currentFilters.categories = Array.from(categoryCheckboxesContainer.querySelectorAll('input[name="product_cat"]:checked')) .map(cb => cb.value); numProductsToShow = PRODUCTS_PER_PAGE; // Resetear paginación al cambiar filtro de categoría applyFiltersAndSort(); } }); const createTagButtons = (container, termsData, filterKey, allText) => { if (!container) return; container.innerHTML = ''; const btnAll = document.createElement('button'); btnAll.textContent = allText; btnAll.dataset.filter = 'all'; btnAll.classList.add('active-filter'); container.appendChild(btnAll); termsData.forEach(term => { const btn = document.createElement('button'); btn.textContent = term.name; btn.dataset.filter = term.slug; container.appendChild(btn); }); container.addEventListener('click', (event) => { if (event.target.tagName === 'BUTTON') { const filterValue = event.target.dataset.filter; currentFilters[filterKey] = filterValue; Array.from(container.querySelectorAll('button')).forEach(b => b.classList.remove('active-filter')); event.target.classList.add('active-filter'); numProductsToShow = PRODUCTS_PER_PAGE; // Resetear paginación applyFiltersAndSort(); } }); }; let searchTimeout; searchInput.addEventListener('input', () => { clearTimeout(searchTimeout); searchTimeout = setTimeout(() => { currentFilters.searchTerm = searchInput.value.trim().toLowerCase(); numProductsToShow = PRODUCTS_PER_PAGE; // Resetear paginación applyFiltersAndSort(); }, 300); }); [minPriceInput, maxPriceInput].forEach(input => { input.addEventListener('change', () => { let minVal = minPriceInput.value ? parseFloat(minPriceInput.value) : null; let maxVal = maxPriceInput.value ? parseFloat(maxPriceInput.value) : null; if (minVal !== null && maxVal !== null && minVal > maxVal) { if (input === minPriceInput) maxVal = minVal; else minVal = maxVal; minPriceInput.value = minVal; maxPriceInput.value = maxVal; } currentFilters.minPrice = minVal; currentFilters.maxPrice = maxVal; priceRangeValuesEl.textContent = `Range: ${minVal !== null ? minVal : globalMinPrice} - ${maxVal !== null ? maxVal : globalMaxPrice}`; numProductsToShow = PRODUCTS_PER_PAGE; // Resetear paginación applyFiltersAndSort(); }); }); statusButtonsContainer.addEventListener('click', (event) => { if (event.target.tagName === 'BUTTON') { const button = event.target; const filterType = button.dataset.filterType; const filterValue = button.dataset.filterValue; currentFilters[filterType] = filterValue; statusButtonsContainer.querySelectorAll(`button[data-filter-type="${filterType}"]`).forEach(btn => btn.classList.remove('active-filter')); button.classList.add('active-filter'); numProductsToShow = PRODUCTS_PER_PAGE; // Resetear paginación applyFiltersAndSort(); } }); sortSelect.addEventListener('change', () => { currentFilters.sortBy = sortSelect.value; numProductsToShow = PRODUCTS_PER_PAGE; // Resetear paginación applyFiltersAndSort(); }); const renderProductList = (productsToDisplay) => { productListUL.innerHTML = ''; if (noProductsMessageEl) { noProductsMessageEl.remove(); noProductsMessageEl = null; } const productsToRender = productsToDisplay.slice(0, numProductsToShow); if (productsToRender.length > 0) { productsToRender.forEach(p => productListUL.appendChild(p.element)); } else if (allProductsData.length > 0 && productsToDisplay.length === 0 ) { // Solo mostrar "no products" si la lista filtrada está vacía noProductsMessageEl = document.createElement('div'); noProductsMessageEl.className = 'no-products-found-message woocommerce-info'; noProductsMessageEl.textContent = 'No products found matching your selection.'; productListUL.parentNode.insertBefore(noProductsMessageEl, productListUL); } // Mostrar u ocultar botón "Load More" if (productsToDisplay.length > numProductsToShow) { loadMoreButton.style.display = 'inline-block'; } else { loadMoreButton.style.display = 'none'; } }; const applyFiltersAndSort = () => { updateCategoryCountsAndRender(); let filtered = allProductsData.filter(p => { // Cambiado nombre de variable const categoryMatch = currentFilters.categories.length === 0 || currentFilters.categories.some(selectedCat => p.categories.includes(selectedCat)); const tagMatch = currentFilters.tag === 'all' || p.tags.includes(currentFilters.tag); const searchMatch = currentFilters.searchTerm === '' || p.title.includes(currentFilters.searchTerm); const minPriceMatch = currentFilters.minPrice === null || p.price >= currentFilters.minPrice; const maxPriceMatch = currentFilters.maxPrice === null || p.price a.originalIndex - b.originalIndex); break; case 'featured': filtered.sort((a, b) => (b.isFeatured - a.isFeatured) || (a.originalIndex - b.originalIndex)); break; case 'price_asc': filtered.sort((a, b) => a.price - b.price); break; case 'price_desc': filtered.sort((a, b) => b.price - a.price); break; case 'title_asc': filtered.sort((a, b) => a.title.localeCompare(b.title)); break; case 'title_desc': filtered.sort((a, b) => b.title.localeCompare(a.title)); break; default: filtered.sort((a, b) => a.originalIndex - b.originalIndex); break; } currentlyDisplayedProducts = filtered; // Guardar la lista completa filtrada y ordenada renderProductList(currentlyDisplayedProducts); // Renderizar la porción visible }; const initialCategories = getUniqueTerms(allProductsData, 'categories'); if (initialCategories.length > 0) { createCategoryCheckboxes(categoryCheckboxesContainer, initialCategories); } else { if (categoryFilterSection) categoryFilterSection.style.display = 'none'; } const tags = getUniqueTerms(allProductsData, 'tags'); if (tags.length > 0) { createTagButtons(tagButtonsContainer, tags, 'tag', 'All'); } else { if(tagFilterSection) tagFilterSection.style.display = 'none'; } applyFiltersAndSort(); });Get Started
From idea to finished design in under five minutes.
Step 1 | Browse our catalog and pick the template that fits your project.
Step 2 | Checkout with 256-bit SSL encryption—your payment is safe and instant.
Step 3 | Download the high-resolution PDF and print or share instantly.
Testimonials
We have a genuine customer base for our AI services and we are grateful to receive their feedback on our service.
“Downloaded the baby-shower game pack, tweaked the colors in Canva and printed the same day. Guests thought I hired a designer!”
Sarah K.
Event Planner, CA
“These templates saved me hours on my pitch deck. Dropped in my logo, exported to PDF, done—looked totally pro.”
Jake M
TexasSmall-Business Owner, TX
“The educational worksheets are a lifesaver for last-minute lessons. Editable and kid-friendly—my class loves them.”
Emily P.
Elementary Teacher, UK
“I needed fresh social slides fast. Found them here, customized in minutes and impressed the client the same afternoon.”
Carlos R.
Digital Marketer, Toronto
“The seating-chart printables matched my couple’s theme perfectly. Instant download = no production delays.”
Lisa H.
Wedding Planner, NY
“Used the planner layouts to launch my printable shop. Quality is top-notch and the ZIP file arrived instantly.”
David S.
Etsy Seller, AUS
Get 10% off your first purchase and be the first to know about new packs.