共计 9755 个字符,预计需要花费 25 分钟才能阅读完成。
如何写一个树
预览地址 — 写 jquery 写吐了 虽然自己一知半解,求解救用过 vue 懂 react 基本全家桶 … 首先 我是用 jquery 写这个树的。同时 我又借鉴了一点状态管理的, 及树中每一步操作都对应一种状态(state)。
例如展开、可编辑、等等,同时整个树又是一个链式的数据(data)表现形式。data + state === 当前树的展现形式。(data+state 去实时渲染整棵树 tree)
例如这个就是数据 + 状态
const jsonStr = ‘[{“id”:”38142133078884677″,”v”:” 手动录入 – 节点一 ”,”hide”: true,”isEdit”:0,”mustHasChild”:true,”childs”:[{“id”:”803762845546041″,”v”:” 手动录入 – 节点 一二 ”,”hide”:false,”isEdit”:0,”mustHasChild”:true,”childs”:[{“id”:”3944279213460402″,”v”:” 手动录入节点一三 ”,”hide”:true,”isEdit”:0,”mustHasChild”:false}]}]},{“id”:”5862194447042262″,”v”:” 手动插入中加节点 – 哈哈 ”,”isEdit”:0,”hide”:false,”mustHasChild”:true,”childs”:[{“id”:”42013322589153246″,”v”:” 哈哈节点 – 二 ”,”hide”:false,”isEdit”:0,”mustHasChild”:true,”childs”:[{“id”:”6335429123251242″,”v”:” 哈哈节点 – 三 ”,”hide”:false,”isEdit”:0,”mustHasChild”:false}]}]},{“id”:”31479025610098965″,”v”:” 手动录入 – 节点 –2 一 ”,”isEdit”:0,”hide”:false,”mustHasChild”:true,”childs”:[{“id”:”8950193765660441″,”v”:” 手动录入 – 节点 –3 一二 ”,”hide”:false,”isEdit”:0,”mustHasChild”:true,”childs”:[{“id”:667316874516471,”v”:” 手动录入 – 节点 –4- 一三.1″,”hide”:true,”isEdit”:0,”mustHasChild”:false},{“id”:1836765245385621,”v”:” 手动录入节点 –4- 一 lll 三.2″,”isEdit”:0,”hide”:true,”mustHasChild”:false},{“id”:”6313844231015344″,”v”:” 手动录入节点 –5–|| 三.3″,”isEdit”:0,”hide”:true,”mustHasChild”:false}]}]},{“id”:”9689285835248749″,”v”:” 我看过的书 ”,”isEdit”:0,”hide”:false,”mustHasChild”:true,”childs”:[{“id”:7093197279146648,”v”:” 红与黑 –“,”hide”:true,”isEdit”:0,”mustHasChild”:true,”childs”:[]},{“id”:”592633265305597″,”v”:” 福尔摩斯 ”,”isEdit”:0,”hide”:false,”mustHasChild”:true,”childs”:[{“id”:”1452411391392623″,”v”:” 柯南安道尔 ”,”hide”:true,”isEdit”:0,”mustHasChild”:false}]},{“id”:”5152351555037056″,”v”:” 三国演义 ”,”isEdit”:0,”hide”:false,”mustHasChild”:true,”childs”:[{“id”:”9845976738760909″,”v”:” 罗贯中 ”,”hide”:true,”isEdit”:0,”mustHasChild”:false}]},{“id”:28281704596645696,”v”:” 基督山 ”,”isEdit”:0,”hide”:false,”mustHasChild”:true,”childs”:[{“id”:”19550230773665422″,”v”:” 大仲马 ”,”hide”:true,”isEdit”:0,”mustHasChild”:false}]},{“id”:”4760492443681241″,”v”:” 天龙八部 ”,”isEdit”:0,”hide”:false,”mustHasChild”:true,”childs”:[{“id”:”1761555398118413″,”v”:” 金庸 ”,”hide”:true,”isEdit”:0,”mustHasChild”:false}]},{“id”:”7629598641567743″,”v”:” 西游记 ”,”isEdit”:0,”hide”:false,”mustHasChild”:true,”childs”:[{“id”:”31575029673804056″,”v”:” 时间点 ”,”hide”:false,”isEdit”:0,”mustHasChild”:false}]},{“id”:”5172793580965298″,”v”:” 红楼梦 ”,”isEdit”:0,”hide”:false,”mustHasChild”:true,”childs”:[{“id”:”4267265911888425″,”v”:” 曹雪芹 ”,”hide”:true,”isEdit”:0,”mustHasChild”:false}]}]}]’;
代码:
const checkHasChild = (curDeep) => {
return curDeep < 3
};
const jsonStr = ‘[{“id”:”38142133078884677″,”v”:” 手动录入 – 节点一 ”,”hide”: true,”isEdit”:0,”mustHasChild”:true,”childs”:[{“id”:”803762845546041″,”v”:” 手动录入 – 节点 一二 ”,”hide”:false,”isEdit”:0,”mustHasChild”:true,”childs”:[{“id”:”3944279213460402″,”v”:” 手动录入节点一三 ”,”hide”:true,”isEdit”:0,”mustHasChild”:false}]}]},{“id”:”5862194447042262″,”v”:” 手动插入中加节点 – 哈哈 ”,”isEdit”:0,”hide”:false,”mustHasChild”:true,”childs”:[{“id”:”42013322589153246″,”v”:” 哈哈节点 – 二 ”,”hide”:false,”isEdit”:0,”mustHasChild”:true,”childs”:[{“id”:”6335429123251242″,”v”:” 哈哈节点 – 三 ”,”hide”:false,”isEdit”:0,”mustHasChild”:false}]}]},{“id”:”31479025610098965″,”v”:” 手动录入 – 节点 –2 一 ”,”isEdit”:0,”hide”:false,”mustHasChild”:true,”childs”:[{“id”:”8950193765660441″,”v”:” 手动录入 – 节点 –3 一二 ”,”hide”:false,”isEdit”:0,”mustHasChild”:true,”childs”:[{“id”:667316874516471,”v”:” 手动录入 – 节点 –4- 一三.1″,”hide”:true,”isEdit”:0,”mustHasChild”:false},{“id”:1836765245385621,”v”:” 手动录入节点 –4- 一 lll 三.2″,”isEdit”:0,”hide”:true,”mustHasChild”:false},{“id”:”6313844231015344″,”v”:” 手动录入节点 –5–|| 三.3″,”isEdit”:0,”hide”:true,”mustHasChild”:false}]}]},{“id”:”9689285835248749″,”v”:” 我看过的书 ”,”isEdit”:0,”hide”:false,”mustHasChild”:true,”childs”:[{“id”:7093197279146648,”v”:” 红与黑 –“,”hide”:true,”isEdit”:0,”mustHasChild”:true,”childs”:[]},{“id”:”592633265305597″,”v”:” 福尔摩斯 ”,”isEdit”:0,”hide”:false,”mustHasChild”:true,”childs”:[{“id”:”1452411391392623″,”v”:” 柯南安道尔 ”,”hide”:true,”isEdit”:0,”mustHasChild”:false}]},{“id”:”5152351555037056″,”v”:” 三国演义 ”,”isEdit”:0,”hide”:false,”mustHasChild”:true,”childs”:[{“id”:”9845976738760909″,”v”:” 罗贯中 ”,”hide”:true,”isEdit”:0,”mustHasChild”:false}]},{“id”:28281704596645696,”v”:” 基督山 ”,”isEdit”:0,”hide”:false,”mustHasChild”:true,”childs”:[{“id”:”19550230773665422″,”v”:” 大仲马 ”,”hide”:true,”isEdit”:0,”mustHasChild”:false}]},{“id”:”4760492443681241″,”v”:” 天龙八部 ”,”isEdit”:0,”hide”:false,”mustHasChild”:true,”childs”:[{“id”:”1761555398118413″,”v”:” 金庸 ”,”hide”:true,”isEdit”:0,”mustHasChild”:false}]},{“id”:”7629598641567743″,”v”:” 西游记 ”,”isEdit”:0,”hide”:false,”mustHasChild”:true,”childs”:[{“id”:”31575029673804056″,”v”:” 时间点 ”,”hide”:false,”isEdit”:0,”mustHasChild”:false}]},{“id”:”5172793580965298″,”v”:” 红楼梦 ”,”isEdit”:0,”hide”:false,”mustHasChild”:true,”childs”:[{“id”:”4267265911888425″,”v”:” 曹雪芹 ”,”hide”:true,”isEdit”:0,”mustHasChild”:false}]}]}]’;
const data = JSON.parse(jsonStr);
window._data = data;
// dir 路径节点 root
// index 当前位置
const createAddBtn = (index, dir) => `<div class=”add-childs-btn” data-index=”${index}” data-dir=”${dir}”> 添加子节点 </div>`;
const createTree = (data, hide, index, dir) => {
hide = undefined === hide ? false : hide;
index = undefined === index ? 0 : index;
dir = undefined === dir ? ” : dir;
const nextIndex = index + 1;
const className = hide ? ‘hide’ : ”;
let htmlStr = `<div class=”tree-fh-node ${className}”>`;
// data
data && data.forEach((d, idx) => {
let {id, v, childs, hide, mustHasChild, isEdit} = d;
let curDir = dir === ” ? `${idx}` : `${dir}-${idx}`;
let deepNum = curDir.toString().split(‘-‘).length;
let showToggleBtnFlag = checkHasChild(deepNum);
let chlidShowClassName = hide ? ‘child-hide’ : ‘child-show’;
isEdit = isEdit === undefined ? 0 : isEdit;
chlidShowClassName = checkHasChild(deepNum) ? chlidShowClassName : ”;
chlidShowClassName = (childs && !childs.length) ? ” : chlidShowClassName;
if (isEdit === 1) {
htmlStr += `<div class=”tree-fh-item ${chlidShowClassName}” data-id=”${id}” data-index=”${index}” data-dir=”${curDir}” data-isedit=”${isEdit}”>`+
(showToggleBtnFlag ? `<span class=”tree-handle-toggle” data-id=”${id}” data-index=”${index}” data-dir=”${curDir}”>${hide ? ‘+’ : ‘-‘}</span>` : ”) +
`<input value=”${v}” data-id=”${id}” data-dir=”${curDir}” data-role=”edit”/></div>`
} else {
htmlStr += `<div class=”tree-fh-item ${chlidShowClassName}” data-id=”${id}” data-index=”${index}” data-dir=”${curDir}”>`+
(showToggleBtnFlag ? `<span class=”tree-handle-toggle” data-id=”${id}” data-index=”${index}” data-dir=”${curDir}”>${hide ? ‘+’ : ‘-‘}</span>` : ”) +
`<span>${v}</span>`+
`<i href=”javascript:;”` +
`data-id=”${id}”` +
`data-index=”${index}”` +
`data-dir=”${curDir}”` +
`data-role=”handle-item”` +
`> 操作 </i>` +
`</div>`;
}
if (childs && childs.length > 0) {
htmlStr += createTree(childs, hide, nextIndex, curDir);
} else {
if(mustHasChild) {
htmlStr += createAddBtn(nextIndex, curDir)
}
}
});
if (0 === index && data.length === 0) {
htmlStr += createAddBtn(0, ”);
};
return htmlStr += `</div>`
};
const createdId = () => {
return Math.random().toString().replace(/0\.0?/, ”);
};
//
const bindEditInput = () => {
$(‘#app’).find(‘[data-role=”edit”]’).focus().on(‘keydown’, function (e) {
const $el = $(this);
const curDir = $el.data(‘dir’).toString();
let id = $el.data(‘id’);
const v = $el.val();
const idxS = curDir.split(‘-‘);
const lg = idxS.length;
const {keyCode} = e;
let curItem = null;
if (id === ”) {
id = createdId();
console.log(‘ 新建 ’);
} else {
console.log(‘ 修改 ’);
}
idxS.forEach((key, idx) => {
if (idx === 0) {
curItem = data[key]
} else {
curItem = curItem.childs[key];
}
});
if (keyCode === 13) {
Object.assign(curItem, {
id,
v: v,
isEdit: 0,
mustHasChild: checkHasChild(lg)
});
renderTree(data);
}
});
};
const bindHandleBtn = () => {
const createHandleBlock = (style) => `<section class=”handle-block”><a href=”javascript:;” data-role=”edit”> 编辑 </a><a href=”javascript:;” data-role=”add”> 增加 </a><a href=”javascript:;” data-role=”del”> 删除 </a></section>`
$(‘[data-role=”handle-item”]’).on(‘click’, function (e) {
const $el = $(this);
const position = $el.position();
const width = $el.outerWidth();
const $hasBlock = $el.find(‘.handle-block’);
e.stopPropagation();
if ($hasBlock.length === 1) {
$hasBlock.remove()
} else {
$(createHandleBlock()).appendTo($el).css({
position: ‘absolute’,
top: `0`,
left: `${width + 2}px`
}).on(‘click’, function (e) {
e.stopPropagation();
console.log(‘..o’)
const {srcElement} = e.originalEvent;
const $el = $(this);
const $parent = $el.parent();
const dir = $parent.data(‘dir’).toString();
const idxS = dir.split(‘-‘).map(idx => Number(idx));
const $srcElement = $(srcElement);
let curItem = null;
let parentItem = null;
let lg = idxS.length;
idxS.forEach((key, idx) => {
if (idx === 0) {
curItem = data[key];
if(lg === 1) {
parentItem = data;
}
} else {
if (idx === lg – 1) {
parentItem = curItem;
}
curItem = curItem.childs[key];
}
});
if ($srcElement.is(‘[data-role=edit]’)) {
console.log(‘edit’);
Object.assign(curItem, {
isEdit: 1
})
};
if ($srcElement.is(‘[data-role=add]’)) {
const curIdx = idxS.pop();
(parentItem.childs || parentItem).splice(curIdx + 1, 0, {
id: createdId(),
v: createdId().substr(0, 5),
isEdit: 0,
hide: true,
mustHasChild: checkHasChild(lg)
});
};
if ($srcElement.is(‘[data-role=del]’)) {
const curIdx = idxS.pop();
(parentItem.childs || parentItem).splice(curIdx, 1);
}
$parent.remove();
renderTree(data);
})
}
});
};
const renderTree = (data) => {
$(‘#app’).html(createTree(data));
bindEditInput();
bindHandleBtn();
};
const bindEvent = () => {
$(‘#app’).on(‘click’, ‘.tree-handle-toggle[data-isedit!=1]’, function () {
const $el = $(this);
const id = $el.data(‘id’);
const dir = $el.data(‘dir’).toString();
const idxS = dir.split(‘-‘);
let curItem = null;
idxS.forEach((key, idx) => {
if (idx === 0) {
curItem = data[key]
} else {
curItem = curItem.childs[key];
}
});
const isHide = curItem.hide;
curItem.hide = !isHide;
renderTree(data);
}).on(‘click’, ‘.add-childs-btn’, function () {
const $el = $(this);
const dir = $el.data(‘dir’).toString();
const idxS = dir.split(‘-‘);
const lg = idxS.length;
const item = {
id: createdId(),
v: createdId().substr(0, 5),
hide: true,
isEdit: 0,
mustHasChild: checkHasChild(lg + 1)
};
let curItem = null;
console.log(‘lg’, lg, idxS, dir);
if (dir === ”) {
data.push(item)
} else {
let curItem = null;
// 0 1 2 ===== 1
idxS.forEach((key, idx) => {
if (idx === 0) {
curItem = data[key]
} else {
curItem = curItem.childs[key];
}
});
curItem && Object.assign(curItem, {
hide: false,
childs: [item]
});
};
renderTree(data);
});
};
bindEvent();
renderTree(data);