静态网站HTML/CSS/JS复用方法及基于Firebase的多Material Design卡片实现问题
Hey there! Let's break down your two questions and walk through practical solutions for each—they’re both common pain points, but totally fixable.
Static sites don’t have server-side rendering by default, but there are simple, effective ways to avoid repeating code:
Use HTML
<template>Tags + Vanilla JS
The<template>tag is a hidden gem for static sites. It lets you define reusable HTML chunks (like navbars, footers, or card layouts) that stay hidden until you clone and inject them with JS. Example:<!-- Define your reusable template --> <template id="navbar-template"> <nav class="site-nav"> <a href="/">Home</a> <a href="/about">About</a> </nav> </template> <!-- Where you want the navbar to appear --> <div id="navbar-container"></div> <script> // Inject the navbar into the container const template = document.getElementById('navbar-template'); const container = document.getElementById('navbar-container'); const navbarClone = document.importNode(template.content, true); container.appendChild(navbarClone); </script>Fetch Reusable HTML Snippets
For larger chunks (like a full footer), save the content as a separate file (e.g.,footer.html) and load it withfetch:async function loadFooter() { const response = await fetch('/footer.html'); const footerHTML = await response.text(); document.getElementById('footer-container').innerHTML = footerHTML; } loadFooter();Static Site Generators (SSGs) for Scale
If your site is growing, use an SSG like Eleventy or Jekyll. These tools let you include reusable components during the build process (so you still get static HTML output). For example, in Eleventy, you can use:{% include "navbar.njk" %}to inject a navbar snippet into every page.
CSS/JS Reuse
- For CSS: Use external stylesheets (link them in every page) and component-based classes (e.g.,
.mdc-cardfor all your Material Design cards) to avoid repeating styles. - For JS: Extract common functions into a separate file (e.g.,
utils.js) and import it where needed:import { loadNavbar, formatCardData } from './utils.js';
- For CSS: Use external stylesheets (link them in every page) and component-based classes (e.g.,
Hardcoding cards for each ID is messy—instead, build a system that generates cards on demand when users input an ID. Here’s a step-by-step solution:
Step 1: Set Up Your HTML Structure
First, create a hidden template for your Material Design card, an input area for users, and a container to hold all cards:
<!-- Card template (hidden by default) --> <template id="md-card-template"> <div class="mdc-card"> <div class="mdc-card__content"> <h3 class="card-id"></h3> <p class="card-data"></p> </div> <div class="mdc-card__actions"> <button class="remove-btn">Remove Card</button> </div> </div> </template> <!-- User input area --> <div class="input-section"> <input type="text" id="doc-id-input" placeholder="Enter Document ID"> <button id="add-card-btn">Add Card</button> </div> <!-- Container for all generated cards --> <div id="cards-container"></div>
Step 2: Write the Dynamic Card Logic
Use JavaScript to fetch data from Firestore, clone the template, fill it with data, and add it to the container. Include error handling for invalid IDs:
// Get references to DOM elements const cardTemplate = document.getElementById('md-card-template'); const cardsContainer = document.getElementById('cards-container'); const addBtn = document.getElementById('add-card-btn'); const idInput = document.getElementById('doc-id-input'); // Function to fetch data and create a card async function createCard(docId) { try { // Replace with your actual Firestore collection reference const docRef = db.collection('your-collection-name').doc(docId); const docSnap = await docRef.get(); if (!docSnap.exists) { alert(`Oops! No document found with ID: ${docId}`); return; } // Extract your string data from the document const cardText = docSnap.data().your-string-field; // Update to your field name // Clone the template and fill in data const cardClone = document.importNode(cardTemplate.content, true); cardClone.querySelector('.card-id').textContent = `Document ID: ${docId}`; cardClone.querySelector('.card-data').textContent = cardText; // Add remove functionality const removeBtn = cardClone.querySelector('.remove-btn'); removeBtn.addEventListener('click', () => { removeBtn.closest('.mdc-card').remove(); }); // Add the card to the container cardsContainer.appendChild(cardClone); // Clear the input field idInput.value = ''; } catch (error) { console.error('Error loading card:', error); alert('Failed to load card—check the console for details.'); } } // Add event listener to the "Add Card" button addBtn.addEventListener('click', () => { const docId = idInput.value.trim(); if (docId) { createCard(docId); } else { alert('Please enter a valid document ID!'); } }); // Optional: Let users press Enter to add a card idInput.addEventListener('keypress', (e) => { if (e.key === 'Enter') addBtn.click(); });
Step 3: Add Material Design Styling
Make sure your CSS includes basic Material Design card styles (adjust to match your existing CSS):
.mdc-card { max-width: 360px; margin: 1rem; padding: 1.5rem; border-radius: 8px; box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15); background: white; } .mdc-card__content { margin-bottom: 1rem; } .card-id { margin: 0 0 0.5rem 0; font-size: 1.1rem; color: #2d3748; } .card-data { margin: 0; color: #4a5568; line-height: 1.5; } .remove-btn { background: none; border: none; color: #2b6cb0; cursor: pointer; padding: 0.5rem 1rem; text-transform: uppercase; font-size: 0.9rem; }
内容的提问来源于stack exchange,提问作者jonasxd360

