sticky-footer实现记录

本文介绍了如何使用 CSS 实现 Sticky Footer 布局效果,让页脚在内容不足时固定于屏幕底部,在内容超出时随页面滚动。通过设置 `.sticky-box` 的 `min-height` 和调整 `.sticky-footer` 的 `margin`,可以轻松实现这一效果。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

sticky-footer是css中的一个经典问题:
当页面内容超出屏幕,页脚模块会像正常页面一样,被推到内容下方,需要拖动滚动条才能看到。
而当页面内容小于屏幕高度,页脚模块会固定在屏幕底部,就像是底边距为零的固定定位。

html:

<div class="detailWrapper" v-show="detailShow">
  <div class="sticky-box clearfix">
      <div class="sticky-content">
    内容
      </div>
    </div>
    <div class="sticky-footer">
     <span class="icon-close" @click="showDetail"></span>
    </div>
  </div>

css:

.detailWrapper
    position fixed
    z-index: 1
    top 0
    left 0
    width 100%
    height 100%
    background rgba(7,7,27,0.8)
    overflow auto
    .sticky-box
      width 100%
      min-height 100%
      .sticky-content
        margin-top 64px
        padding-bottom 64px
    .sticky-footer
      margin -64px auto 0 auto
      width 32px
      height 32px
      line-height 32px
      font-size 32px
      color rgba(255,255,255,0.5)
      cursor pointer

相关介绍文章:
https://www.cnblogs.com/wisewrong/p/6525696.html
https://www.w3cplus.com/css3/css-secrets/sticky-footers.html

以下代碼在content-display的部份在footer增加一個切換按鈕,除了原本顯示格式外,可切換成帶有html標籤的格式: <template> <div> <div> <div class="wangeditor"> <WangEditor v-model="content" @response="(msg) => content = msg" /> </div> <div class="right-panel"> <mreditor1 ref="mreditor1" /> </div> </div> <div v-if="isVisible" class="content-display" v-html="highlightedContent"></div> <footer class="sticky-footer"> <span><button @click="toggleContent">顯示/隱藏標籤</button></span> <span><button @click="resetAll" class="reset-btn">輸入新醫案</button></span> <!-- 新增ID输入框和获取按钮 --> <span class="id-input"> <input v-model="fetchId" placeholder="輸入醫案ID" type="number" min="1" /> <button @click="fetchById">獲取醫案</button> </span> <!-- 操作按钮组 --> <span v-if="submittedId"> <button @click="updateContent" class="update-btn">更新醫案</button> <button @click="deleteContent" class="delete-btn">刪除醫案</button> </span> <span v-else> <button @click="submitContent" class="submit-btn">提交醫案</button> </span> <span v-if="submittedId" class="submitted-id">醫案 ID: {{ submittedId }}</span> <span>醫案編輯器</span> </footer> </div> </template> <script> import WangEditor from './WangEditor.vue'; import mreditor1 from './mreditor1.vue'; export default { components: { WangEditor, mreditor1 }, data() { return { content: '', isVisible: true, submittedId: null, fetchId: null, dnTags: [] // 存储从API获取的标签数据 }; }, computed: { // 计算高亮后的内容 highlightedContent() { if (!this.dnTags.length || !this.content) return this.content; // 创建临时DOM元素处理高亮 const tempEl = document.createElement('div'); tempEl.innerHTML = this.content; // 遍历所有文本节点 const walker = document.createTreeWalker( tempEl, NodeFilter.SHOW_TEXT ); const nodes = []; while (walker.nextNode()) { nodes.push(walker.currentNode); } // 对每个文本节点进行处理 nodes.forEach(node => { let text = node.nodeValue; let newHtml = text; // 对每个标签进行高亮处理(按长度降序排序,避免短标签优先匹配) this.dnTags .slice() .sort((a, b) => b.length - a.length) .forEach(tag => { const regex = new RegExp( escapeRegExp(tag), 'g' ); newHtml = newHtml.replace( regex, `<span style="color: rgb(212, 107, 8); font-weight: bold;">${tag}</span>` ); }); // 如果内容有变化则替换节点 if (newHtml !== text) { const span = document.createElement('span'); span.innerHTML = newHtml; node.parentNode.replaceChild(span, node); } }); return tempEl.innerHTML; } }, async mounted() { // 组件挂载时获取标签数据 await this.fetchDNTags(); }, methods: { // 获取标签数据 async fetchDNTags() { try { const response = await fetch('DNTag/?format=json'); const data = await response.json(); this.dnTags = data .map(item => item.dnname) .filter(name => name && name.trim().length > 0); } catch (error) { console.error('获取标签失败:', error); alert('标签数据加载失败,高亮功能不可用'); } }, // 通过ID获取医案数据 async fetchById() { if (!this.fetchId) { alert('請輸入有效的醫案ID'); return; } try { const response = await fetch(`MRInfo/${this.fetchId}/?format=json`); if (response.ok) { const data = await response.json(); // 填充表单数据 this.$refs.mreditor1.formData.mrname = data.mrname || ''; this.$refs.mreditor1.formData.mrposter = data.mrposter || ''; this.content = data.mrcase || ''; this.submittedId = data.id; this.fetchId = null; // 清空输入框 // 刷新标签数据 await this.fetchDNTags(); alert('醫案數據加載成功!'); } else if (response.status === 404) { alert('未找到該ID的醫案'); } else { throw new Error('獲取醫案失敗'); } } catch (error) { console.error('Error:', error); alert(`獲取醫案失敗: ${error.message}`); } }, // 提交医案 async submitContent() { const formData = this.$refs.mreditor1.getFormData(); const postData = { mrcase: this.content, ...formData }; try { const response = await fetch('MRInfo/?format=json', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify(postData), }); if(response.ok) { const data = await response.json(); this.submittedId = data.id; alert('醫案提交成功!'); } else { throw new Error('提交失败'); } } catch (error) { console.error('Error:', error); alert(`提交失败: ${error.message}`); } }, // 更新医案 async updateContent() { if (!this.submittedId) return; const formData = this.$refs.mreditor1.getFormData(); const postData = { mrcase: this.content, ...formData }; try { const response = await fetch(`MRInfo/${this.submittedId}/?format=json`, { method: 'PUT', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify(postData), }); if (response.ok) { alert('醫案更新成功!'); } else { throw new Error('更新失败'); } } catch (error) { console.error('Error:', error); alert(`更新失败: ${error.message}`); } }, // 删除医案 async deleteContent() { if (!this.submittedId) return; if (!confirm('確定要刪除這個醫案嗎?')) return; try { const response = await fetch(`MRInfo/${this.submittedId}/?format=json`, { method: 'DELETE', headers: { 'Content-Type': 'application/json', } }); if (response.ok) { this.resetAll(); alert('醫案刪除成功!'); } else { throw new Error('刪除失败'); } } catch (error) { console.error('Error:', error); alert(`刪除失败: ${error.message}`); } }, // 重置表单 resetAll() { this.content = ''; this.submittedId = null; this.fetchId = null; this.$refs.mreditor1.resetForm(); }, // 切换内容显示 toggleContent() { this.isVisible = !this.isVisible; } } }; // 辅助函数:转义正则特殊字符 function escapeRegExp(string) { return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); } </script> <style scoped> /* 原有样式保持不变 */ .wangeditor { flex: 1; padding: 10px; overflow-y: auto; } .right-panel { position: fixed; top: 56px; bottom: 45px; right: 0; width: 30%; background: white; padding: 10px; z-index: 100; overflow-y: auto; } .content-display { position: fixed; top: 570px; left: 0; width: 70%; bottom: 45px; z-index: 999; background-color: white; overflow-y: auto; padding: 10px; border: 1px solid #eee; } .sticky-footer { display: flex; justify-content: flex-end; align-items: center; position: fixed; bottom: 0; left: 0; width: 100%; background-color: #ffd800ff; z-index: 999; padding: 10px 20px; box-sizing: border-box; flex-wrap: wrap; } .sticky-footer > span { margin-left: 5px; display: flex; align-items: center; } .submitted-id { padding: 2px; background-color: #e2f0fd; color: #004085; border-radius: 4px; } .reset-btn { margin-left: 10px; padding: 2px; background-color: #dc3545; color: white; border: none; border-radius: 4px; cursor: pointer; } .reset-btn:hover { background-color: #c82333; } /* 新增ID输入框样式 */ .id-input { display: flex; align-items: center; } .id-input input { width: 100px; padding: 2px 5px; margin-right: 5px; border: 1px solid #ccc; border-radius: 4px; } /* 操作按钮样式 */ .submit-btn { background-color: #28a745; color: white; border: none; padding: 2px 8px; border-radius: 4px; cursor: pointer; } .update-btn { background-color: #007bff; color: white; border: none; padding: 2px 8px; border-radius: 4px; cursor: pointer; } .delete-btn { background-color: #dc3545; color: white; border: none; padding: 2px 8px; border-radius: 4px; cursor: pointer; } .submit-btn:hover { background-color: #218838; } .update-btn:hover { background-color: #0069d9; } .delete-btn:hover { background-color: #c82333; } </style>
07-16
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值