123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020 |
- let nowFileName = "";
- let jsonInfo = {};
- let tabId = "";
- let nodeObj;
- let jslist;
- let cssList;
- let cssFileInfo = {}
- let oldFile; // 记录上一次点击的文件信息
- // 记录页卡信息
- // let pageTab = new PageTab();
- chrome.runtime.onMessage.addListener(async (request, sender, sendResponse) => {
- if (request.msgToPopup === "发送页面") {
- cssFileInfo = {}
- //初始化JS代码编辑器
- window.jsCode = new CodeEditor();
- jsCode.InitEditor($("#jscode")[0], false, false, [], false);
- jsCode.dataType = 3;
- InitTable();
- // 结构接收的信息
- let { jslist: _jslist, targe, csslist, tabId: _tabId, path } = request.data;
- jslist = _jslist;
- tabId = _tabId;
- cssList = csslist; // 页面的所有样式信息
- parseCodeToAST(
- jslist.map((item) => {
- return {
- code: item.data,
- name: item.fileName,
- };
- })
- );
- let data = window.findSelector();
- let id = $(targe).attr("id");
- let classList = $(targe).attr("class").split(" ");
- data = data.filter((m) => {
- let v = m.过滤器;
- if (v.includes(id)) return true;
- if (classList.length > 0) {
- for (const classitem of classList) {
- if (v.includes(classitem)) return true;
- }
- }
- });
- await attach();
- await getPathNodeInfo(path);
- let events = await findEvent();
- TabInstance.option({
- dataSource: data,
- });
- let cssJsonData = CssJsonData(jsonInfo)
- CSSTabInstance.option({
- dataSource: cssJsonData,
- });
- eventTableInstance.option({
- dataSource: translateEventData(events),
- });
- }
- });
- function InitTable() {
- window.TabInstance = $("#filterTable")
- .dxDataGrid({
- dataSource: [],
- keyExpr: "resource_detail_id",
- remoteOperations: false,
- searchPanel: {
- visible: true,
- highlightCaseSensitive: true,
- },
- groupPanel: { visible: false },
- grouping: {
- allowCollapsing: true,
- autoExpandAll: true,
- expandMode: "rowClick",
- },
- selection: {
- mode: "single",
- },
- allowColumnReordering: true,
- rowAlternationEnabled: true,
- showBorders: true,
- height: "100%",
- columns: [
- {
- dataField: "JS文件",
- dataType: "JS文件",
- groupIndex: 0
- },
- {
- dataField: "DOM操作分类",
- dataType: "DOM操作分类",
- },
- {
- dataField: "过滤器",
- caption: "过滤器",
- },
- {
- dataField: "所在位置",
- dataType: "所在位置",
- },
- ],
- onContentReady(e) { },
- onSelectionChanged: function (selectedItems) {
- let data = selectedItems.selectedRowsData[0];
- if (data) {
- oldFile = ""
- jsCode.dataType = 3;
- let ast = data.astJSON;
- if (data.JS文件 !== nowFileName) {
- nowFileName = data.JS文件;
- let code = window.fileList.find((o) => o.name === data.JS文件).code;
- //更改JS代码
- jsCode.SetCode(code);
- }
- //jscode定位
- let {
- defineNode: {
- iid,
- loc: { end, start },
- },
- } = data.source_data;
- {
- //移出所有高亮
- jsCode.RemoveAllHighlight();
- //设置高亮
- jsCode.CreateHighlight({
- startLine: start?.line,
- startColumn: start?.column + 1,
- endLine: end?.line,
- endColumn: end?.column + 1,
- });
- }
- }
- },
- })
- .dxDataGrid("instance");
- window.CSSTabInstance = $("#cssJson")
- .dxDataGrid({
- dataSource: [],
- keyExpr: "id",
- remoteOperations: false,
- searchPanel: {
- visible: true,
- highlightCaseSensitive: true,
- },
- selection: {
- mode: "single",
- },
- groupPanel: { visible: false },
- grouping: {
- allowCollapsing: true,
- autoExpandAll: true,
- expandMode: "rowClick",
- },
- allowColumnReordering: true,
- rowAlternationEnabled: true,
- showBorders: true,
- height: "100%",
- paging: {
- enabled: false,
- pageSize: 0,
- },
- columns: [
- {
- dataField: "文件",
- caption: "来源文件",
- groupCellTemplate: function (tdom, tdMsg) {
- let name = tdMsg.value;
- try {
- name = (name === "用户自定义" ? name : name.match(/\/([^\/]+\.css)(\?[^ ]*)?$/)[1])
- } catch {
- }
- tdom.text(name)
- },
- groupIndex: 0
- },
- {
- dataField: "分类",
- caption: "分类"
- },
- {
- dataField: "选择器",
- caption: "选择器"
- }, {
- dataField: "样式属性",
- caption: "样式属性",
- cellTemplate: function (tdom, tdMsg) {
- let styles = tdMsg.value;
- let html = styles.map(({ name, value }) => `<li>${name}: ${value}</li>`).join('')
- tdom.html(`<ul>${html}</ul>`)
- }
- },
- ],
- onSelectionChanged: async function (selectedItems) {
- let data = selectedItems.selectedRowsData[0];
- if (data) {
- if (data.文件) {
- let fileName = data.文件;
- let code = ""
- if (!cssFileInfo[fileName]) {
- code = fileName === "用户自定义" ? "" : await (await fetch(fileName)).text();
- cssFileInfo[fileName] = {
- code
- }
- } else {
- code = cssFileInfo[fileName].code
- }
- // 设置显示css
- jsCode.dataType = 2;
- if (oldFile !== fileName) {
- //更改JS代码
- jsCode.SetCode(code);
- }
- //移出所有高亮
- jsCode.RemoveAllHighlight();
- oldFile = fileName;
- // 定位使用
- let selectors = [];
- if (!cssFileInfo[fileName].selectors) {
- selectors = await cssCodeToAST(jsCode.GetCode());
- cssFileInfo[fileName].selectors = selectors
- } else {
- selectors = cssFileInfo[fileName].selectors
- }
- let location = selectors.find(a => a.name === data.选择器);
- if (location) {
- setTimeout(() => {
- let { loc: { start, end } } = location;
- //设置高亮
- jsCode.CreateHighlight({
- startLine: start?.line,
- startColumn: start?.column,
- endLine: end?.line,
- endColumn: end?.column,
- });
- }, 500);
- }
- }
- }
- },
- })
- .dxDataGrid("instance");
- window.eventTableInstance = $("#eventTable")
- .dxDataGrid({
- dataSource: [],
- keyExpr: "id",
- remoteOperations: false,
- searchPanel: {
- visible: true,
- highlightCaseSensitive: true,
- },
- selection: {
- mode: "single",
- },
- allowColumnReordering: true,
- rowAlternationEnabled: true,
- showBorders: true,
- paging: {
- enabled: false,
- pageSize: 0,
- },
- height: "100%",
- columns: [
- {
- dataField: "事件名称",
- caption: "事件名称",
- },
- {
- dataField: "绑定对象",
- caption: "绑定对象",
- },
- {
- dataField: "事件文件",
- caption: "事件文件",
- },
- ],
- })
- .dxDataGrid("instance");
- }
- function findPropRef(key, json) {
- let matchedCSSRules = json.matchedCSSRules;
- let retArr = [];
- for (const matchedCSSRule of matchedCSSRules) {
- let cssProperties = matchedCSSRule.rule.style.cssProperties;
- let cssProp = cssProperties.find((m) => m.name == key);
- if (!cssProp) continue;
- let ret = {
- 来源: "CSS",
- 选择器: matchedCSSRule.rule.selectorList.text,
- 选中选择器: matchedCSSRule.matchingSelectors[0],
- 属性值: cssProp.value,
- };
- retArr.push(ret);
- }
- return retArr;
- }
- async function cssCodeToAST(cssCode) {
- let curAst = csstree.parse(cssCode, {
- parseAtrulePrelude: false,
- parseRulePrelude: false,
- parseValue: false,
- positions: true,
- });
- let selectors = [];
- //https://github.com/csstree/csstree/blob/master/docs/parsing.md#parsesource-options
- await csstree.walk(curAst, (node) => {
- if (node.type === 'Rule')//node.type === 'Declaration'
- {
- let curSelector;
- if (node.prelude.children) {
- curSelector = node.prelude.children.map(child => child.raw);
- }
- else {
- curSelector = node.prelude.value;
- }
- let loc = node.prelude.loc;
- if (curSelector) selectors.push({
- //name: curSelector,//curSelector,
- name: curSelector.replaceAll("\r\n*", " ").replaceAll("\r\n", " "),
- sourceName: cssCode.substring(loc.start.offset, loc.end.offset),
- loc: loc,
- });
- }
- });
- return selectors;
- }
- async function getPathNodeInfo(path) {
- const doc = await getDocument();
- // const htmlInfo = doc.children.find((m) => m.localName === "html");
- let info = doc;
- for (const index of path) {
- info = getpath(info, index);
- }
- nodeObj = info;
- let nodeId = info.nodeId;
- let cssInfo = await getMatchedStylesForNode(nodeId);
- jsonInfo = cssInfo;
- function getpath(info, index) {
- let children = info.children;
- return children[index];
- }
- }
- async function getMatchedStylesForNode(nodeId) {
- let ret = await chrome.debugger.sendCommand(
- { tabId },
- "CSS.getMatchedStylesForNode",
- {
- nodeId,
- }
- );
- return ret;
- }
- async function getDocument() {
- const doc = await chrome.debugger.sendCommand({ tabId }, "DOM.getDocument", {
- pierce: true,
- depth: -1,
- });
- return doc.root;
- }
- async function attach() {
- // const targets = await chrome.debugger.getTargets();
- // let target = targets.find((m) => m.tabId == tabId);
- // if (target?.attached === true) {
- // } else {
- // await chrome.debugger.attach({ tabId }, "1.3");
- // }
- await chrome.debugger.attach({ tabId }, "1.3").catch(() => { });
- await chrome.debugger.sendCommand({ tabId }, "DOM.enable");
- await chrome.debugger.sendCommand({ tabId }, "CSS.enable");
- await chrome.debugger.sendCommand({ tabId }, "Debugger.enable");
- }
- async function findEvent() {
- let winObj = await chrome.debugger.sendCommand(
- { tabId },
- "Runtime.evaluate",
- {
- expression: "self",
- objectGroup: "",
- includeCommandLineAPI: false,
- silent: true,
- returnByValue: false,
- generatePreview: false,
- userGesture: false,
- awaitPromise: false,
- }
- );
- let winEvents = await chrome.debugger.sendCommand(
- { tabId },
- "DOMDebugger.getEventListeners",
- {
- objectId: winObj.result.objectId,
- }
- );
- let documentObj = await chrome.debugger.sendCommand(
- { tabId },
- "Runtime.evaluate",
- {
- expression: "document",
- objectGroup: "",
- includeCommandLineAPI: false,
- silent: true,
- returnByValue: false,
- generatePreview: false,
- userGesture: false,
- awaitPromise: false,
- }
- );
- let documentEvents = await chrome.debugger.sendCommand(
- { tabId },
- "DOMDebugger.getEventListeners",
- {
- objectId: documentObj.result.objectId,
- }
- );
- let selfObj = await chrome.debugger.sendCommand(
- { tabId },
- "DOM.resolveNode",
- {
- nodeId: nodeObj.nodeId,
- }
- );
- let selfEvents = await chrome.debugger.sendCommand(
- { tabId },
- "DOMDebugger.getEventListeners",
- {
- objectId: selfObj.object.objectId,
- }
- );
- for (const listener of winEvents.listeners) {
- await listenerGetFile(listener);
- }
- for (const listener of documentEvents.listeners) {
- await listenerGetFile(listener);
- }
- for (const listener of selfEvents.listeners) {
- await listenerGetFile(listener);
- }
- return {
- winEvents: winEvents.listeners,
- documentEvents: documentEvents.listeners,
- selfEvents: selfEvents.listeners,
- };
- }
- async function listenerGetFile(listener) {
- let ret = await chrome.debugger.sendCommand(
- { tabId },
- "Debugger.getScriptSource",
- {
- scriptId: listener.scriptId,
- }
- );
- let js = jslist.find((m) => m.data == ret.scriptSource);
- if (js) {
- js.scriptId = listener.scriptId;
- listener.fileName = js.fileName;
- listener.fileUrl = js.url;
- listener.fileData = js.data;
- }
- }
- function translateEventData(eventObj) {
- let { winEvents, documentEvents, selfEvents } = eventObj;
- let result = [];
- result.push(
- ...winEvents.map((even) => ({
- 事件名称: even.type,
- 绑定对象: "window",
- 事件文件: even.fileName,
- }))
- );
- result.push(
- ...documentEvents.map((even) => ({
- 事件名称: even.type,
- 绑定对象: "document",
- 事件文件: even.fileName,
- }))
- );
- result.push(
- ...selfEvents.map((even) => ({
- 事件名称: even.type,
- 绑定对象: "selft",
- 事件文件: even.fileName,
- }))
- );
- return result.map((a, index) => ({ ...a, id: index }));
- }
- window.onload = function () {
- InitTabOperation();
- };
- /**
- * 设置操作页卡功能
- */
- function InitTabOperation() {
- document.querySelector(".tabs").addEventListener("click", function (e) {
- let target = e.target;
- if (target.classList.contains("tab")) {
- showContent(target);
- }
- });
- }
- function showContent(tab) {
- let id = tab.dataset.id; // 获取点击的页卡ID
- // 获取选中的页卡元素
- let activeTab = document.querySelector(".tabs .active");
- if (activeTab !== null) {
- if (id === activeTab.dataset.id) return;
- activeTab.classList.remove("active");
- }
- // 获取选中的容器的元素
- let activeContent = document.querySelector(".tab-container .active");
- if (activeContent !== null) {
- if (activeContent.id === id) return;
- activeContent.classList.remove("active");
- }
- // 给新选中的页卡加入选中效果
- tab.classList.add("active");
- let curActiveContent = document.querySelector(`.tab-container #${id}`);
- if (curActiveContent !== null) {
- curActiveContent.classList.add("active");
- }
- }
- /**
- * 接收转换后有用的json数据
- * @param {} jsonData
- */
- function CssJsonData(jsonData) {
- let data = CssJsonRules.parseJson(jsonData);
- let { 伪类的样式, 内敛的样式, 匹配的样式, 继承的样式 } = data;
- let result = [];
- 伪类的样式.forEach((rule) => {
- let { matchs, pseudoType } = rule;
- matchs.forEach(match => {
- result.push({
- 文件: match.origin,
- 样式属性: match.style,
- 选择器: match.text,
- 分类: "伪类样式"
- })
- })
- });
- if (内敛的样式.length > 0) {
- result.push({
- 文件: "用户自定义",
- 样式属性: 内敛的样式,
- 选择器: "",
- 分类: "内敛样式"
- })
- }
- 匹配的样式.forEach((rule) => {
- result.push({
- 文件: rule.origin,
- 样式属性: rule.style,
- 选择器: rule.text,
- 分类: "外敛样式"
- })
- })
- 继承的样式.forEach((rule) => {
- result.push({
- 文件: rule.origin,
- 样式属性: rule.style,
- 选择器: rule.text,
- 分类: "继承样式"
- })
- })
- return result.map((a, index) => ({ ...a, id: index }));
- }
- /**
- * css基础权重规则
- */
- class CSSSRule {
- static calculateSpecificity(selector) {
- // 初始化权重
- let ids = 0;
- let classes = 0;
- let tags = 0;
- // 去掉多余的空格
- const parts = selector.trim().split(/\s+/);
- parts.forEach(part => {
- // 检查每个选择器部分
- if (part.startsWith('#')) {
- ids += 1; // ID选择器
- } else if (part.startsWith('.')) {
- classes += 1; // 类选择器
- } else {
- tags += 1; // 标签选择器
- }
- });
- // 返回权重对象
- // 计算最终权重值
- const specificityValue = (ids * 100) + (classes * 10) + tags;
- return specificityValue;
- }
- static cacheStyle = {}
- static getStyleHref(styleId, selectorText) {
- if (CSSSRule.cacheStyle[styleId]) return CSSSRule.cacheStyle[styleId];
- foo: for (let sheet of cssList) {
- try {
- let { href, selectorTexts } = sheet;
- for (let text of selectorTexts)
- if (selectorText === text) {
- CSSSRule.cacheStyle[styleId] = href;
- break foo;
- }
- } catch { }
- }
- return CSSSRule.cacheStyle[styleId]
- }
- // css属性扩展
- static cssExtent = [
- {
- "property": "margin",
- "sub-properties": [
- "margin-top",
- "margin-right",
- "margin-bottom",
- "margin-left"
- ]
- },
- {
- "property": "padding",
- "sub-properties": [
- "padding-top",
- "padding-right",
- "padding-bottom",
- "padding-left"
- ]
- },
- {
- "property": "border",
- "sub-properties": [
- "border-width",
- "border-style",
- "border-color",
- "border-top",
- "border-right",
- "border-bottom",
- "border-left"
- ]
- },
- {
- "property": "border-style",
- "sub-properties": [
- "border-top-style",
- "border-right-style",
- "border-bottom-style",
- "border-left-style"
- ]
- },
- {
- "property": "border-top",
- "sub-properties": [
- "border-top-width",
- "border-top-style",
- "border-top-color"
- ]
- },
- {
- "property": "border-bottom",
- "sub-properties": [
- "border-bottom-width",
- "border-bottom-style",
- "border-bottom-color"
- ]
- },
- {
- "property": "border-left",
- "sub-properties": [
- "border-left-width",
- "border-left-style",
- "border-left-color"
- ]
- },
- {
- "property": "border-right",
- "sub-properties": [
- "border-right-width",
- "border-right-style",
- "border-right-color"
- ]
- },
- {
- "property": "border-radius",
- "sub-properties": [
- "border-top-left-radius",
- "border-top-right-radius",
- "border-bottom-right-radius",
- "border-bottom-left-radius"
- ]
- },
- {
- "property": "background",
- "sub-properties": [
- "background-color",
- "background-image",
- "background-repeat",
- "background-position",
- "background-size",
- "background-attachment",
- "background-clip",
- "background-origin"
- ]
- },
- {
- "property": "font",
- "sub-properties": [
- "font-style",
- "font-variant",
- "font-weight",
- "font-size",
- "line-height",
- "font-family"
- ]
- },
- {
- "property": "list-style",
- "sub-properties": [
- "list-style-type",
- "list-style-position",
- "list-style-image"
- ]
- },
- {
- "property": "text-decoration",
- "sub-properties": [
- "text-decoration-line",
- "text-decoration-color",
- "text-decoration-style",
- "text-decoration-thickness"
- ]
- },
- {
- "property": "transition",
- "sub-properties": [
- "transition-property",
- "transition-duration",
- "transition-timing-function",
- "transition-delay"
- ]
- },
- {
- "property": "animation",
- "sub-properties": [
- "animation-name",
- "animation-duration",
- "animation-timing-function",
- "animation-delay",
- "animation-iteration-count",
- "animation-direction",
- "animation-fill-mode",
- "animation-play-state"
- ]
- },
- {
- "property": "grid",
- "sub-properties": [
- "grid-template-rows",
- "grid-template-columns",
- "grid-template-areas",
- "grid-area",
- "grid-column",
- "grid-row",
- "grid-auto-rows",
- "grid-auto-columns",
- "grid-auto-flow"
- ]
- },
- {
- "property": "flex",
- "sub-properties": [
- "flex-grow",
- "flex-shrink",
- "flex-basis"
- ]
- },
- {
- "property": "outline",
- "sub-properties": [
- "outline-width",
- "outline-style",
- "outline-color"
- ]
- }
- ]
- }
- /**
- * 根据json导出数据
- */
- class CssJsonRules {
- static findExtentInfo(entries) {
- return entries.map(e => {
- let extentInfo = CSSSRule.cssExtent.find(a => a.property === e.name);
- if (extentInfo) {
- return extentInfo["sub-properties"];
- }
- return undefined
- }).filter(Boolean).flat();
- }
- /**
- * 解析json数据
- * @param {*} jsonData
- * @returns
- */
- static parseJson(jsonData) {
- let { inlineStyle, matchedCSSRules, pseudoElements, inherited } = jsonData;
- let result = {
- "内敛的样式": [],
- "匹配的样式": [],
- "伪类的样式": [],
- "继承的样式": []
- }
- // 解析json中的内敛样式
- result.内敛的样式.push(...inlineStyle.cssProperties.filter(a => a.text !== undefined).map(({ name, value }) => ({ name, value })));
- // 解析json中的匹配样式
- matchedCSSRules.forEach(cssRule => {
- let { rule } = cssRule;
- let { shorthandEntries } = rule;
- // 按照规律查询出信息
- let style = rule.style.cssProperties.filter(a => a.text !== undefined || rule.origin === "user-agent").map(({ name, value }) => ({ name, value }));
- if (rule.origin === "user-agent" && shorthandEntries && shorthandEntries.length > 0) {
- let sameInfo = CssJsonRules.findExtentInfo(shorthandEntries);
- style = style.filter(s => !sameInfo.includes(s.name))
- }
- let text = rule.selectorList.text;
- let obj = {
- origin: rule.styleSheetId ? CSSSRule.getStyleHref(rule.styleSheetId, text) : '用户自定义',
- text,
- style
- };
- result.匹配的样式.push(obj)
- })
- // 解析伪类的样式
- pseudoElements.forEach(cssRule => {
- let { pseudoType, matches } = cssRule;
- let obj = {
- pseudoType,
- matchs: matches.map(({ rule }) => {
- let text = rule.selectorList.text;
- return {
- origin: rule.styleSheetId ? CSSSRule.getStyleHref(rule.styleSheetId, text) : '用户自定义',
- text,
- style: rule.style.cssProperties.filter(a => a.text !== undefined).map(({ name, value }) => ({ name, value }))
- }
- })
- };
- result.伪类的样式.push(obj)
- })
- // 解析继承的样式
- inherited.forEach(inheritedCssRule => {
- let matchedCSSRules = inheritedCssRule.matchedCSSRules;
- matchedCSSRules.forEach((cssRule) => {
- let { rule } = cssRule;
- let style = rule.style.cssProperties.filter(a => a.text !== undefined).map(({ name, value }) => ({ name, value }));
- if (style.length === 0) return;
- let text = rule.selectorList.text;
- result.继承的样式.push({
- origin: rule.styleSheetId ? CSSSRule.getStyleHref(rule.styleSheetId, text) : '用户自定义',
- text,
- style
- })
- })
- })
- return result;
- }
- }
- /**
- * 页卡组件
- */
- class PageTab {
- #tabs = []
- #tabContainer
- constructor({ tabs }) {
- this.#tabs = tabs || [];
- this.#tabContainer = document.querySelector('.tabs');
- this.#initClickEvent();
- }
- addTab(tabInfo) {
- this.#tabs.push(tabInfo)
- }
- #initClickEvent() {
- this.#tabContainer.addEventListener('click', (e) => {
- let target = e.target;
- if (target.classList.contains("tab")) {
- this.activeTabById(target.dataset.id);
- }
- })
- }
- get tabs() {
- return this.#tabs;
- }
- activeTabById(id) {
- // 移除已经选中的
- let activedTab = this.tabs.find(a => a.isActive);
- if (activedTab) {
- activedTab.removeActive();
- }
- // 给设置的选中
- let awaitActiveTab = this.tabs.find(a => a.id === id);
- if (awaitActiveTab) {
- awaitActiveTab.addActive();
- }
- }
- }
- class tab {
- #id // 页卡id
- #tabEle // tab页卡元素
- #contentEle // tab内容元素
- #isAlived // 是否保持持久化(为true表示 页卡激活事件只执行一次,当前内容会一直存在,如果为false,则表示每次点击页卡都会重新执行激活事件)
- activeEvent // 激活页卡执行事件
- #isActived // 是否选中
- constructor({ id, isAlived, activeEvent }) {
- this.#id = id;
- this.#tabEle = tab.getTabEleById(id);
- this.#contentEle = tab.getTabContentById(id);
- this.#isAlived = isAlived;
- this.#isActived = false;
- this.activeEvent = activeEvent;
- }
- get tabInfo() {
- return {
- isActive: this.isActive(),
- id: this.#id
- }
- }
- /**
- * 判断是否为选中的标签
- */
- isActive() {
- return this.#isActived;
- }
- /**
- * 添加选中效果
- */
- addActive() {
- this.#tabEle.classList.add('active');
- this.#contentEle.classList.add('active');
- this.#isActived = true;
- this.activeEvent();
- }
- activeEvent() {
- if (this.#isAlived === true) {
- // 如果开启了持久化,表示只执行一次
- this.activeEvent = function () { }
- }
- typeof this.activeEvent === 'function' && this.activeEvent();
- }
- /**
- * 移除选中效果
- */
- removeActive() {
- this.#tabEle.classList.remove('active');
- this.#contentEle.classList.remove('active');
- this.#isActived = false;
- }
- static getTabEleById(id) {
- return document.querySelector(`.tabs tab[data-id="${id}"]`);
- }
- static getTabContentById(id) {
- return document.querySelector(`.tab-container #${id}`);
- }
- }
|