如何写一个树预览地址 – 写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) => &lt;div class="add-childs-btn" data-index="${index}" data-dir="${dir}"&gt;添加子节点&lt;/div&gt;; 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 = &lt;div class="tree-fh-node ${className}"&gt;; // 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 += &lt;div class="tree-fh-item ${chlidShowClassName}" data-id="${id}" data-index="${index}" data-dir="${curDir}" data-isedit="${isEdit}"&gt;+ (showToggleBtnFlag ? &lt;span class="tree-handle-toggle" data-id="${id}" data-index="${index}" data-dir="${curDir}"&gt;${hide ? '+' : '-'}&lt;/span&gt; : ‘’) + &lt;input value="${v}" data-id="${id}" data-dir="${curDir}" data-role="edit"/&gt;&lt;/div&gt; } else { htmlStr += &lt;div class="tree-fh-item ${chlidShowClassName}" data-id="${id}" data-index="${index}" data-dir="${curDir}"&gt;+ (showToggleBtnFlag ? &lt;span class="tree-handle-toggle" data-id="${id}" data-index="${index}" data-dir="${curDir}"&gt;${hide ? '+' : '-'}&lt;/span&gt; : ‘’) + &lt;span&gt;${v}&lt;/span&gt;+ &lt;i href="javascript:;" + data-id="${id}" + data-index="${index}" + data-dir="${curDir}" + data-role="handle-item" + &gt;操作&lt;/i&gt; + &lt;/div&gt;; } 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 += &lt;/div&gt; }; 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) => &lt;section class="handle-block"&gt;&lt;a href="javascript:;" data-role="edit"&gt;编辑&lt;/a&gt;&lt;a href="javascript:;" data-role="add"&gt;增加&lt;/a&gt;&lt;a href="javascript:;" data-role="del"&gt;删除&lt;/a&gt;&lt;/section&gt; $(’[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);