首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 網頁程式設計 >> PHP 程式設計 >> PHP 相關知識

類 Google iGoogle 門戶佈局:可拖拽模塊與狀態持久化保存方案

     編輯:PHP 相關知識

項目背景

早期 Web 門戶(如 Google iGoogle、Netvibes)普及了「可拖拽、可折疊、個性化佈局」的儀表盤模式。本文分析一份基於 HTML Table + JavaScript 實現的雙欄拖拽門戶頁面結構,探討如何保存用戶最後的佈局狀態。

頁面結構解析

提供的代碼為靜態 HTML 片段,核心特徵:

  • 雙欄佈局:外層 #parentTable 兩列 td[width="48%"],各自包含多個 class="dragTable" 表格作為可拖拽模塊容器。
  • 模塊標題欄:每個 dragTable 首行 class="dragTR" 包含摺疊、添加、更多操作圖標,作為拖拽手柄。
  • 模塊 ID:id="kpis", id="4graph", id="favorite", id="1graph" 等唯一標識,用於標識模塊類型與順序。
  • 內容區:次行 bgcolor="#FFFFFF" 顯示具體內容或「無任何記錄」佔位。

實現拖拽排序的關鍵技術點(原生 JS 方案)

  1. 事件監聽:在 dragTR 上綁定 mousedown,document 上綁定 mousemove/mouseup。
  2. 視覺反饋:拖拽時創建半透明克隆節點或顯示佔位符,計算目標插入位置(insertBefore)。
  3. 同欄/跨欄:判斷鼠標座標屬於左欄還是右欄 td,移動對應的 dragTable 節點。
  4. 狀態序列化:拖拽結束後,遍歷左右兩欄收集模塊 ID 及順序、摺疊狀態,生成 JSON:
    {
      "left":  [{"id":"kpis", "collapsed":false}, {"id":"4graph", "collapsed":true}],
      "right": [{"id":"favorite", "collapsed":false}, {"id":"1graph", "collapsed":false}]
    }
  5. 持久化存儲:
    • 後端方案:AJAX POST 到 /api/portal/layout,綁定用戶 ID 存入 MySQL/Redis。
    • 前端方案:localStorage.setItem('portal_layout', json),無需登錄即可記憶(但換瀏覽器失效)。
  6. 頁面初始化還原:頁面加載時讀取存儲數據,按順序 appendChild 重建 DOM 結構,恢復摺疊類名。

現代化重構建議

維度原代碼 (2000s 風格)現代最佳實踐
佈局Table + 固定寬度CSS Grid / Flexbox + 響應式斷點
拖拽原生 mousedown/move/up 手寫SortableJS、Vue Draggable、React Beautiful DnD
組件化內聯 HTML 字符串Vue/React/Svelte 組件,Props 驅動渲染
狀態管理全局變量 + localStoragePinia/Redux/Zustand + 持久化中間件
模塊註冊硬編碼 ID插件機制:定義 PortalModule 接口,動態註冊/發現

核心邏輯代碼骨架(原生 JS 精簡版)

<script>
// 配置
const STORAGE_KEY = 'my_portal_layout_v1';
const leftCol  = document.getElementById('left-column');  // 對應左側 td
const rightCol = document.getElementById('right-column'); // 對應右側 td

// 1. 初始化:從 localStorage 恢復
function restoreLayout() {
  const saved = JSON.parse(localStorage.getItem(STORAGE_KEY) || 'null');
  if (!saved) return;
  ['left','right'].forEach(side => {
    const col = side === 'left' ? leftCol : rightCol;
    saved[side].forEach(mod => {
      const el = document.getElementById(mod.id);
      if (el) {
        col.appendChild(el); // 移動現有節點到正確順序
        if (mod.collapsed) el.classList.add('collapsed');
      }
    });
  });
}

// 2. 拖拽核心 (精簡,生產建議用 SortableJS)
let dragged = null;
document.querySelectorAll('.dragTR').forEach(handle => {
  handle.addEventListener('mousedown', e => {
    dragged = handle.closest('.dragTable');
    dragged.classList.add('dragging');
    document.body.style.cursor = 'grabbing';
  });
});

document.addEventListener('mousemove', e => {
  if (!dragged) return;
  // 簡易:懸停在哪個 column 上就移入該 column 末尾
  const overCol = e.target.closest('#left-column, #right-column');
  if (overCol && !overCol.contains(dragged)) {
    overCol.appendChild(dragged);
  }
});

document.addEventListener('mouseup', () => {
  if (!dragged) return;
  dragged.classList.remove('dragging');
  document.body.style.cursor = '';
  saveLayout();
  dragged = null;
});

// 3. 摺疊切換
document.querySelectorAll('.dragTR [onclick*="collapseExpand"]').forEach(btn => {
  btn.onclick = e => {
    e.target.closest('.dragTable').classList.toggle('collapsed');
    saveLayout();
  };
});

// 4. 保存
function saveLayout() {
  const layout = { left: [], right: [] };
  ['left','right'].forEach(side => {
    const col = side === 'left' ? leftCol : rightCol;
    col.querySelectorAll('.dragTable').forEach(tbl => {
      layout[side].push({
        id: tbl.id,
        collapsed: tbl.classList.contains('collapsed')
      });
    });
  });
  localStorage.setItem(STORAGE_KEY, JSON.stringify(layout));
  console.log('Layout saved:', layout);
}

// 入口
restoreLayout();
</script>

總結

從「Table 拖拽」到「組件化儀表盤」,核心需求不變:用戶自定義佈局 + 狀態持久化。理解原始碼的 DOM 結構與狀態序列化邏輯,有助於在現代框架中設計更優雅的 Portal/Dashboard 系統。關鍵在於:唯一 ID 標識模塊、順序數組表示佈局、布爾值表示摺疊、存儲介質解耦。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved