我有一个包含对象和数组的嵌套数据结构。我如何提取信息,即访问一个特定的或多个值(或键)?
例如:
var data = {
code: 42,
items: [{
id: 1,
name: 'foo'
}, {
id: 2,
name: 'bar'
}]
};
我如何访问项目中的第二个项目的名称?
我有一个包含对象和数组的嵌套数据结构。我如何提取信息,即访问一个特定的或多个值(或键)?
例如:
var data = {
code: 42,
items: [{
id: 1,
name: 'foo'
}, {
id: 2,
name: 'bar'
}]
};
我如何访问项目中的第二个项目的名称?
当前回答
如果您试图在JSON字符串中查找路径,可以将数据转储到https://jsonpathfinder.com并单击GUI元素。它将生成元素路径的JS语法。
除此之外,对于您可能想要迭代的任何数组,用循环替换相关的数组偏移下标,如[0]。
这里有一个简单版本的工具,你可以在这里或在https://ggorlen.github.io/json-dive/上运行。单击要将路径复制到剪贴板的节点。
/* code minified to make the tool easier to run without having to scroll */ let bracketsOnly=!1,lastHighlighted={style:{}};const keyToStr=t=>!bracketsOnly&&/^[a-zA-Z_$][a-zA-Z$_\d]*$/.test(t)?`.${toHTML(t)}`:`["${toHTML(t)}"]`,pathToData=t=>`data-path="data${t.join("")}"`,htmlSpecialChars={"&":"&","<":"<",">":">",'"':""","'":"'","\t":"\\t","\r":"\\r","\n":"\\n"," ":" "},toHTML=t=>(""+t).replace(/[&<>"'\t\r\n ]/g,t=>htmlSpecialChars[t]),makeArray=(t,e)=>`\n [<ul ${pathToData(e)}>\n ${t.map((t,a)=>{e.push(`[${a}]`);const n=`<li ${pathToData(e)}>\n ${pathify(t,e).trim()},\n </li>`;return e.pop(),n}).join("")}\n </ul>]\n`,makeObj=(t,e)=>`\n {<ul ${pathToData(e)}>\n ${Object.entries(t).map(([t,a])=>{e.push(keyToStr(t));const n=`<li ${pathToData(e)}>\n "${toHTML(t)}": ${pathify(a,e).trim()},\n </li>`;return e.pop(),n}).join("")}\n </ul>}\n`,pathify=(t,e=[])=>Array.isArray(t)?makeArray(t,e):"object"==typeof t&&t!=null?makeObj(t,e):toHTML("string"==typeof t?`"${t}"`:t),defaultJSON='{\n "corge": "test JSON... \\n asdf\\t asdf",\n "foo-bar": [\n {"id": 42},\n [42, {"foo": {"baz": {"ba r<>!\\t": true, "4quux": "garply"}}}]\n ]\n}',$=document.querySelector.bind(document),$$=document.querySelectorAll.bind(document),resultEl=$("#result"),pathEl=$("#path"),tryToJSON=t=>{try{resultEl.innerHTML=pathify(JSON.parse(t)),$("#error").innerText=""}catch(t){resultEl.innerHTML="",$("#error").innerText=t}},copyToClipboard=t=>{const e=document.createElement("textarea");e.textContent=t,document.body.appendChild(e),e.select(),document.execCommand("copy"),document.body.removeChild(e)},flashAlert=(t,e=2e3)=>{const a=document.createElement("div");a.textContent=t,a.classList.add("alert"),document.body.appendChild(a),setTimeout(()=>a.remove(),e)},handleClick=t=>{t.stopPropagation(),copyToClipboard(t.target.dataset.path),flashAlert("copied!"),$("#path-out").textContent=t.target.dataset.path},handleMouseOut=t=>{lastHighlighted.style.background="transparent",pathEl.style.display="none"},handleMouseOver=t=>{pathEl.textContent=t.target.dataset.path,pathEl.style.left=`${t.pageX+30}px`,pathEl.style.top=`${t.pageY}px`,pathEl.style.display="block",lastHighlighted.style.background="transparent",(lastHighlighted=t.target.closest("li")).style.background="#0ff"},handleNewJSON=t=>{tryToJSON(t.target.value),[...$$("#result *")].forEach(t=>{t.addEventListener("click",handleClick),t.addEventListener("mouseout",handleMouseOut),t.addEventListener("mouseover",handleMouseOver)})};$("textarea").addEventListener("change",handleNewJSON),$("textarea").addEventListener("keyup",handleNewJSON),$("textarea").value=defaultJSON,$("#brackets").addEventListener("change",t=>{bracketsOnly=!bracketsOnly,handleNewJSON({target:{value:$("textarea").value}})}),handleNewJSON({target:{value:defaultJSON}}); /**/ *{box-sizing:border-box;font-family:monospace;margin:0;padding:0}html{height:100%}#path-out{background-color:#0f0;padding:.3em}body{margin:0;height:100%;position:relative;background:#f8f8f8}textarea{width:100%;height:110px;resize:vertical}#opts{background:#e8e8e8;padding:.3em}#opts label{padding:.3em}#path{background:#000;transition:all 50ms;color:#fff;padding:.2em;position:absolute;display:none}#error{margin:.5em;color:red}#result ul{list-style:none}#result li{cursor:pointer;border-left:1em solid transparent}#result li:hover{border-color:#ff0}.alert{background:#f0f;padding:.2em;position:fixed;bottom:10px;right:10px} <!-- --> <div class="wrapper"><textarea></textarea><div id="opts"><label>brackets only: <input id="brackets"type="checkbox"></label></div><div id="path-out">click a node to copy path to clipboard</div><div id="path"></div><div id="result"></div><div id="error"></div></div>
Unminified(也可以在GitHub上找到):
let bracketsOnly = false; let lastHighlighted = {style: {}}; const keyToStr = k => !bracketsOnly && /^[a-zA-Z_$][a-zA-Z$_\d]*$/.test(k) ? `.${toHTML(k)}` : `["${toHTML(k)}"]` ; const pathToData = p => `data-path="data${p.join("")}"`; const htmlSpecialChars = { "&": "&", "<": "<", ">": ">", '"': """, "'": "'", "\t": "\\t", "\r": "\\r", "\n": "\\n", " ": " ", }; const toHTML = x => ("" + x) .replace(/[&<>"'\t\r\n ]/g, m => htmlSpecialChars[m]) ; const makeArray = (x, path) => ` [<ul ${pathToData(path)}> ${x.map((e, i) => { path.push(`[${i}]`); const html = `<li ${pathToData(path)}> ${pathify(e, path).trim()}, </li>`; path.pop(); return html; }).join("")} </ul>] `; const makeObj = (x, path) => ` {<ul ${pathToData(path)}> ${Object.entries(x).map(([k, v]) => { path.push(keyToStr(k)); const html = `<li ${pathToData(path)}> "${toHTML(k)}": ${pathify(v, path).trim()}, </li>`; path.pop(); return html; }).join("")} </ul>} `; const pathify = (x, path=[]) => { if (Array.isArray(x)) { return makeArray(x, path); } else if (typeof x === "object" && x !== null) { return makeObj(x, path); } return toHTML(typeof x === "string" ? `"${x}"` : x); }; const defaultJSON = `{ "corge": "test JSON... \\n asdf\\t asdf", "foo-bar": [ {"id": 42}, [42, {"foo": {"baz": {"ba r<>!\\t": true, "4quux": "garply"}}}] ] }`; const $ = document.querySelector.bind(document); const $$ = document.querySelectorAll.bind(document); const resultEl = $("#result"); const pathEl = $("#path"); const tryToJSON = v => { try { resultEl.innerHTML = pathify(JSON.parse(v)); $("#error").innerText = ""; } catch (err) { resultEl.innerHTML = ""; $("#error").innerText = err; } }; const copyToClipboard = text => { const ta = document.createElement("textarea"); ta.textContent = text; document.body.appendChild(ta); ta.select(); document.execCommand("copy"); document.body.removeChild(ta); }; const flashAlert = (text, timeoutMS=2000) => { const alert = document.createElement("div"); alert.textContent = text; alert.classList.add("alert"); document.body.appendChild(alert); setTimeout(() => alert.remove(), timeoutMS); }; const handleClick = e => { e.stopPropagation(); copyToClipboard(e.target.dataset.path); flashAlert("copied!"); $("#path-out").textContent = e.target.dataset.path; }; const handleMouseOut = e => { lastHighlighted.style.background = "transparent"; pathEl.style.display = "none"; }; const handleMouseOver = e => { pathEl.textContent = e.target.dataset.path; pathEl.style.left = `${e.pageX + 30}px`; pathEl.style.top = `${e.pageY}px`; pathEl.style.display = "block"; lastHighlighted.style.background = "transparent"; lastHighlighted = e.target.closest("li"); lastHighlighted.style.background = "#0ff"; }; const handleNewJSON = e => { tryToJSON(e.target.value); [...$$("#result *")].forEach(e => { e.addEventListener("click", handleClick); e.addEventListener("mouseout", handleMouseOut); e.addEventListener("mouseover", handleMouseOver); }); }; $("textarea").addEventListener("change", handleNewJSON); $("textarea").addEventListener("keyup", handleNewJSON); $("textarea").value = defaultJSON; $("#brackets").addEventListener("change", e => { bracketsOnly = !bracketsOnly; handleNewJSON({target: {value: $("textarea").value}}); }); handleNewJSON({target: {value: defaultJSON}}); * { box-sizing: border-box; font-family: monospace; margin: 0; padding: 0; } html { height: 100%; } #path-out { background-color: #0f0; padding: 0.3em; } body { margin: 0; height: 100%; position: relative; background: #f8f8f8; } textarea { width: 100%; height: 110px; resize: vertical; } #opts { background: #e8e8e8; padding: 0.3em; } #opts label { padding: 0.3em; } #path { background: black; transition: all 0.05s; color: white; padding: 0.2em; position: absolute; display: none; } #error { margin: 0.5em; color: red; } #result ul { list-style: none; } #result li { cursor: pointer; border-left: 1em solid transparent; } #result li:hover { border-color: #ff0; } .alert { background: #f0f; padding: 0.2em; position: fixed; bottom: 10px; right: 10px; } <div class="wrapper"> <textarea></textarea> <div id="opts"> <label> brackets only: <input id="brackets" type="checkbox"> </label> </div> <div id="path-out">click a node to copy path to clipboard</div> <div id="path"></div> <div id="result"></div> <div id="error"></div> </div>
这并不是为了代替学习如何钓鱼,但一旦你知道了,可以节省时间。
其他回答
老问题,但没有人提到lodash(只是下划线)。
如果你已经在你的项目中使用lodash,我认为在一个复杂的例子中有一种优雅的方法:
选择1
_.get(response, ['output', 'fund', 'data', '0', 'children', '0', 'group', 'myValue'], '')
一样:
选择2
response.output.fund.data[0].children[0].group.myValue
第一个选项和第二个选项之间的区别是,在Opt 1中,如果你在路径中缺少一个属性(未定义),你不会得到错误,它会返回第三个参数。
对于数组过滤器,lodash有_.find(),但我宁愿使用常规的filter()。但我仍然认为上面的方法_.get()在处理非常复杂的数据时非常有用。我在过去遇到过非常复杂的api,它很方便!
我希望它能对那些正在寻找操作标题所暗示的真正复杂数据的选项的人有用。
下面是一个使用对象扫描的答案。
当访问单个条目时,这个答案并没有提供比普通javascript更多的好处。然而,同时与多个字段交互,这个答案可以更有效。
下面是与单个字段交互的方法
// const objectScan = require('object-scan'); const data = { code: 42, items: [{ id: 1, name: 'foo' }, { id: 2, name: 'bar' }] }; const get = (haystack, needle) => objectScan([needle], { abort: true, rtn: 'value' })(haystack); const set = (haystack, needle, value) => objectScan([needle], { abort: true, rtn: 'bool', filterFn: ({ parent, property }) => { parent[property] = value; return true; } })(haystack); console.log(get(data, 'items[1].name')); // => bar console.log(set(data, 'items[1].name', 'foo2')); // => true console.log(data); // => { code: 42, items: [ { id: 1, name: 'foo' }, { id: 2, name: 'foo2' } ] } .as-console-wrapper {max-height: 100% !important; top: 0} <script src="https://bundle.run/object-scan@13.8.0"></script>
声明:我是object-scan的作者
下面是如何同时与多个字段交互
// const objectScan = require('object-scan'); const data = { code: 42, items: [{ id: 1, name: 'foo' }, { id: 2, name: 'bar' }] }; const get = (haystack, ...needles) => objectScan(needles, { joined: true, rtn: 'entry' })(haystack); const set = (haystack, actions) => objectScan(Object.keys(actions), { rtn: 'count', filterFn: ({ matchedBy, parent, property }) => { matchedBy.forEach((m) => { parent[property] = actions[m]; }) return true; } })(haystack); console.log(get(data, 'items[0].name', 'items[1].name')); // => [ [ 'items[1].name', 'bar' ], [ 'items[0].name', 'foo' ] ] console.log(set(data, { 'items[0].name': 'foo1', 'items[1].name': 'foo2' })); // => 2 console.log(data); // => { code: 42, items: [ { id: 1, name: 'foo1' }, { id: 2, name: 'foo2' } ] } .as-console-wrapper {max-height: 100% !important; top: 0} <script src="https://bundle.run/object-scan@13.8.0"></script>
声明:我是object-scan的作者
下面是如何在一个深度嵌套的对象中通过id搜索找到一个实体(正如在评论中问到的那样)
// const objectScan = require('object-scan'); const myData = { code: 42, items: [{ id: 1, name: 'aaa', items: [{ id: 3, name: 'ccc' }, { id: 4, name: 'ddd' }] }, { id: 2, name: 'bbb', items: [{ id: 5, name: 'eee' }, { id: 6, name: 'fff' }] }] }; const findItemById = (haystack, id) => objectScan(['**(^items$).id'], { abort: true, useArraySelector: false, rtn: 'parent', filterFn: ({ value }) => value === id })(haystack); console.log(findItemById(myData, 5)); // => { id: 5, name: 'eee' } .as-console-wrapper {max-height: 100% !important; top: 0} <script src="https://bundle.run/object-scan@13.8.0"></script>
声明:我是object-scan的作者
我就是这么做的。
let groups = [
{
id:1,
title:"Group 1",
members:[
{
id:1,
name:"Aftab",
battry:'10%'
},
{
id:2,
name:"Jamal",
},
{
id:3,
name:"Hamid",
},
{
id:4,
name:"Aqeel",
},
]
},
{
id:2,
title:"Group 2",
members:[
{
id:1,
name:"Aftab",
battry:'10%'
},
{
id:2,
name:"Jamal",
battry:'10%'
},
{
id:3,
name:"Hamid",
},
]
},
{
id:3,
title:"Group 3",
members:[
{
id:1,
name:"Aftab",
battry:'10%'
},
{
id:3,
name:"Hamid",
},
{
id:4,
name:"Aqeel",
},
]
}
]
groups.map((item) => {
// if(item.id == 2){
item.members.map((element) => {
if(element.id == 1){
element.battry="20%"
}
})
//}
})
groups.forEach((item) => {
item.members.forEach((item) => {
console.log(item)
})
})
有时,使用字符串访问嵌套对象是可取的。例如,最简单的方法是第一级
var obj = { hello: "world" };
var key = "hello";
alert(obj[key]);//world
但复杂的json通常不是这样。随着json变得越来越复杂,在json中查找值的方法也变得越来越复杂。导航json的递归方法是最好的,如何利用递归取决于要搜索的数据类型。如果涉及到条件语句,json搜索可能是一个很好的工具。
如果已经知道要访问的属性,但是路径很复杂,例如在这个对象中
var obj = {
arr: [
{ id: 1, name: "larry" },
{ id: 2, name: "curly" },
{ id: 3, name: "moe" }
]
};
你知道你想要得到对象中数组的第一个结果,也许你想使用
var moe = obj["arr[0].name"];
然而,这将导致一个异常,因为对象没有该名称的属性。能够使用这种方法的解决方案是将对象的树形面平直。这可以递归完成。
function flatten(obj){
var root = {};
(function tree(obj, index){
var suffix = toString.call(obj) == "[object Array]" ? "]" : "";
for(var key in obj){
if(!obj.hasOwnProperty(key))continue;
root[index+key+suffix] = obj[key];
if( toString.call(obj[key]) == "[object Array]" )tree(obj[key],index+key+suffix+"[");
if( toString.call(obj[key]) == "[object Object]" )tree(obj[key],index+key+suffix+".");
}
})(obj,"");
return root;
}
现在,这个复杂的物体可以被平面化
var obj = previous definition;
var flat = flatten(obj);
var moe = flat["arr[0].name"];//moe
下面是使用这种方法的jsFiddle演示。
如果您试图在JSON字符串中查找路径,可以将数据转储到https://jsonpathfinder.com并单击GUI元素。它将生成元素路径的JS语法。
除此之外,对于您可能想要迭代的任何数组,用循环替换相关的数组偏移下标,如[0]。
这里有一个简单版本的工具,你可以在这里或在https://ggorlen.github.io/json-dive/上运行。单击要将路径复制到剪贴板的节点。
/* code minified to make the tool easier to run without having to scroll */ let bracketsOnly=!1,lastHighlighted={style:{}};const keyToStr=t=>!bracketsOnly&&/^[a-zA-Z_$][a-zA-Z$_\d]*$/.test(t)?`.${toHTML(t)}`:`["${toHTML(t)}"]`,pathToData=t=>`data-path="data${t.join("")}"`,htmlSpecialChars={"&":"&","<":"<",">":">",'"':""","'":"'","\t":"\\t","\r":"\\r","\n":"\\n"," ":" "},toHTML=t=>(""+t).replace(/[&<>"'\t\r\n ]/g,t=>htmlSpecialChars[t]),makeArray=(t,e)=>`\n [<ul ${pathToData(e)}>\n ${t.map((t,a)=>{e.push(`[${a}]`);const n=`<li ${pathToData(e)}>\n ${pathify(t,e).trim()},\n </li>`;return e.pop(),n}).join("")}\n </ul>]\n`,makeObj=(t,e)=>`\n {<ul ${pathToData(e)}>\n ${Object.entries(t).map(([t,a])=>{e.push(keyToStr(t));const n=`<li ${pathToData(e)}>\n "${toHTML(t)}": ${pathify(a,e).trim()},\n </li>`;return e.pop(),n}).join("")}\n </ul>}\n`,pathify=(t,e=[])=>Array.isArray(t)?makeArray(t,e):"object"==typeof t&&t!=null?makeObj(t,e):toHTML("string"==typeof t?`"${t}"`:t),defaultJSON='{\n "corge": "test JSON... \\n asdf\\t asdf",\n "foo-bar": [\n {"id": 42},\n [42, {"foo": {"baz": {"ba r<>!\\t": true, "4quux": "garply"}}}]\n ]\n}',$=document.querySelector.bind(document),$$=document.querySelectorAll.bind(document),resultEl=$("#result"),pathEl=$("#path"),tryToJSON=t=>{try{resultEl.innerHTML=pathify(JSON.parse(t)),$("#error").innerText=""}catch(t){resultEl.innerHTML="",$("#error").innerText=t}},copyToClipboard=t=>{const e=document.createElement("textarea");e.textContent=t,document.body.appendChild(e),e.select(),document.execCommand("copy"),document.body.removeChild(e)},flashAlert=(t,e=2e3)=>{const a=document.createElement("div");a.textContent=t,a.classList.add("alert"),document.body.appendChild(a),setTimeout(()=>a.remove(),e)},handleClick=t=>{t.stopPropagation(),copyToClipboard(t.target.dataset.path),flashAlert("copied!"),$("#path-out").textContent=t.target.dataset.path},handleMouseOut=t=>{lastHighlighted.style.background="transparent",pathEl.style.display="none"},handleMouseOver=t=>{pathEl.textContent=t.target.dataset.path,pathEl.style.left=`${t.pageX+30}px`,pathEl.style.top=`${t.pageY}px`,pathEl.style.display="block",lastHighlighted.style.background="transparent",(lastHighlighted=t.target.closest("li")).style.background="#0ff"},handleNewJSON=t=>{tryToJSON(t.target.value),[...$$("#result *")].forEach(t=>{t.addEventListener("click",handleClick),t.addEventListener("mouseout",handleMouseOut),t.addEventListener("mouseover",handleMouseOver)})};$("textarea").addEventListener("change",handleNewJSON),$("textarea").addEventListener("keyup",handleNewJSON),$("textarea").value=defaultJSON,$("#brackets").addEventListener("change",t=>{bracketsOnly=!bracketsOnly,handleNewJSON({target:{value:$("textarea").value}})}),handleNewJSON({target:{value:defaultJSON}}); /**/ *{box-sizing:border-box;font-family:monospace;margin:0;padding:0}html{height:100%}#path-out{background-color:#0f0;padding:.3em}body{margin:0;height:100%;position:relative;background:#f8f8f8}textarea{width:100%;height:110px;resize:vertical}#opts{background:#e8e8e8;padding:.3em}#opts label{padding:.3em}#path{background:#000;transition:all 50ms;color:#fff;padding:.2em;position:absolute;display:none}#error{margin:.5em;color:red}#result ul{list-style:none}#result li{cursor:pointer;border-left:1em solid transparent}#result li:hover{border-color:#ff0}.alert{background:#f0f;padding:.2em;position:fixed;bottom:10px;right:10px} <!-- --> <div class="wrapper"><textarea></textarea><div id="opts"><label>brackets only: <input id="brackets"type="checkbox"></label></div><div id="path-out">click a node to copy path to clipboard</div><div id="path"></div><div id="result"></div><div id="error"></div></div>
Unminified(也可以在GitHub上找到):
let bracketsOnly = false; let lastHighlighted = {style: {}}; const keyToStr = k => !bracketsOnly && /^[a-zA-Z_$][a-zA-Z$_\d]*$/.test(k) ? `.${toHTML(k)}` : `["${toHTML(k)}"]` ; const pathToData = p => `data-path="data${p.join("")}"`; const htmlSpecialChars = { "&": "&", "<": "<", ">": ">", '"': """, "'": "'", "\t": "\\t", "\r": "\\r", "\n": "\\n", " ": " ", }; const toHTML = x => ("" + x) .replace(/[&<>"'\t\r\n ]/g, m => htmlSpecialChars[m]) ; const makeArray = (x, path) => ` [<ul ${pathToData(path)}> ${x.map((e, i) => { path.push(`[${i}]`); const html = `<li ${pathToData(path)}> ${pathify(e, path).trim()}, </li>`; path.pop(); return html; }).join("")} </ul>] `; const makeObj = (x, path) => ` {<ul ${pathToData(path)}> ${Object.entries(x).map(([k, v]) => { path.push(keyToStr(k)); const html = `<li ${pathToData(path)}> "${toHTML(k)}": ${pathify(v, path).trim()}, </li>`; path.pop(); return html; }).join("")} </ul>} `; const pathify = (x, path=[]) => { if (Array.isArray(x)) { return makeArray(x, path); } else if (typeof x === "object" && x !== null) { return makeObj(x, path); } return toHTML(typeof x === "string" ? `"${x}"` : x); }; const defaultJSON = `{ "corge": "test JSON... \\n asdf\\t asdf", "foo-bar": [ {"id": 42}, [42, {"foo": {"baz": {"ba r<>!\\t": true, "4quux": "garply"}}}] ] }`; const $ = document.querySelector.bind(document); const $$ = document.querySelectorAll.bind(document); const resultEl = $("#result"); const pathEl = $("#path"); const tryToJSON = v => { try { resultEl.innerHTML = pathify(JSON.parse(v)); $("#error").innerText = ""; } catch (err) { resultEl.innerHTML = ""; $("#error").innerText = err; } }; const copyToClipboard = text => { const ta = document.createElement("textarea"); ta.textContent = text; document.body.appendChild(ta); ta.select(); document.execCommand("copy"); document.body.removeChild(ta); }; const flashAlert = (text, timeoutMS=2000) => { const alert = document.createElement("div"); alert.textContent = text; alert.classList.add("alert"); document.body.appendChild(alert); setTimeout(() => alert.remove(), timeoutMS); }; const handleClick = e => { e.stopPropagation(); copyToClipboard(e.target.dataset.path); flashAlert("copied!"); $("#path-out").textContent = e.target.dataset.path; }; const handleMouseOut = e => { lastHighlighted.style.background = "transparent"; pathEl.style.display = "none"; }; const handleMouseOver = e => { pathEl.textContent = e.target.dataset.path; pathEl.style.left = `${e.pageX + 30}px`; pathEl.style.top = `${e.pageY}px`; pathEl.style.display = "block"; lastHighlighted.style.background = "transparent"; lastHighlighted = e.target.closest("li"); lastHighlighted.style.background = "#0ff"; }; const handleNewJSON = e => { tryToJSON(e.target.value); [...$$("#result *")].forEach(e => { e.addEventListener("click", handleClick); e.addEventListener("mouseout", handleMouseOut); e.addEventListener("mouseover", handleMouseOver); }); }; $("textarea").addEventListener("change", handleNewJSON); $("textarea").addEventListener("keyup", handleNewJSON); $("textarea").value = defaultJSON; $("#brackets").addEventListener("change", e => { bracketsOnly = !bracketsOnly; handleNewJSON({target: {value: $("textarea").value}}); }); handleNewJSON({target: {value: defaultJSON}}); * { box-sizing: border-box; font-family: monospace; margin: 0; padding: 0; } html { height: 100%; } #path-out { background-color: #0f0; padding: 0.3em; } body { margin: 0; height: 100%; position: relative; background: #f8f8f8; } textarea { width: 100%; height: 110px; resize: vertical; } #opts { background: #e8e8e8; padding: 0.3em; } #opts label { padding: 0.3em; } #path { background: black; transition: all 0.05s; color: white; padding: 0.2em; position: absolute; display: none; } #error { margin: 0.5em; color: red; } #result ul { list-style: none; } #result li { cursor: pointer; border-left: 1em solid transparent; } #result li:hover { border-color: #ff0; } .alert { background: #f0f; padding: 0.2em; position: fixed; bottom: 10px; right: 10px; } <div class="wrapper"> <textarea></textarea> <div id="opts"> <label> brackets only: <input id="brackets" type="checkbox"> </label> </div> <div id="path-out">click a node to copy path to clipboard</div> <div id="path"></div> <div id="result"></div> <div id="error"></div> </div>
这并不是为了代替学习如何钓鱼,但一旦你知道了,可以节省时间。