JSON
是一种轻量级的数据交换格式,它有键值对集合(js 中的对象)和数组两种结构。JSON
是一个通用的格式,在前后端语言中都能跟该 JSON
打交道。
有时候我们需要将 JSON
格式输入至页面展示的需求,其中还需要保持一定的索引,那么该如何实现呢?
使用
我们将对象转为 JSON
字符串时会经常使用 JSON.stringify 这个 API,其实该方法就内置有格式化的参数:
1 2 3 4 5
| var userInfo = {name: 'anran758',github: 'https://github.com/anran758'}; var info = JSON.stringify(userInfo, null, 2);
console.log(info);
|
在上面的代码中,我们第一个参数(value
)传入了一个需要序列化的对象。第二个参数是replacer
,用以对属性转换和处理,由于我们不需要额外的处理,因此传入一个null
;第三个参数则是空格索引的个数,封顶是10
,0
或不传则没有空格。
在控制台打印出信息后,我们可以看的出来格式化的数据是带换行符,并且有缩进的格式。接下来我们就要考虑如何输出到页面中。
输出
只要学过HTML
的朋友都知道,我们直接将数据输入至HTML
中,空格缩进会被浏览器给忽略掉的。因此不能输入到 <div>
中。这时候又想到,JSON
格式实际上也算是代码的一种,那能不能输入至雷士代码块的标签中呢?答案是可以的。
HTML
中有两个标签可以展示源代码: <pre>
和 <code>
。它们之间不同之处在于:
<pre>
表示预定义格式文本,按照原文件中的编排,以等宽字体的形式展现出来,文本中的空白符(比如空格和换行符)都会显示出来。<code>
则是呈现一段计算机代码,它以浏览器的默认等宽字体显示,但并不一定会完整呈现原来的格式。
这些标签知识实际上算是比较冷门的知识,或许远古的面试题会考这种知识点,平时很少会遇到。但是如果你经常使用markdown
的话,那么这些标签在markdown
中有不同的别名:
1 2 3
| 比如 markdown 语法中的 ``,实际上等同于 <code> 标签。实际作用是短代码块标签
而 markdown 语法中的长代码块就等同于 `<pre>` 标签,不同的博客或者网站的应用中还可以对 `<pre>` 加类名,用以展示不同的语言的语法高亮。
|
通过三者之间的对比可以看出,只有 <pre>
才是符合我们需求的。
确定好展示的方式后,就可以考虑进一步扩展格式化的功能。比如对象中还有属性是 JSON
字符串的话,咱也进一步的解析,直至最底层。想实现这种功能需要编写一个递归函数,考虑如下代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69
| const isPlainObject = (v) => Object.prototype.toString.call(v) === "[object Object]" const isString = (v) => Object.prototype.toString.call(v) === "[object String]"
function formatJsonStrAsObj(sample) { let temp = sample;
if (isString(temp)) { try { temp = JSON.parse(temp); } catch (ex) { return sample; } }
if (isPlainObject(temp)) { temp = { ...temp };
Object.keys(temp).forEach(key => { const item = temp[key];
if (isString(item) || isPlainObject(item)) { temp[key] = formatJsonStrAsObj(item); } }); }
return temp; }
function formatJSONIndnt(sample, indnt = 2) { const newSample = formatJsonStrAsObj(sample);
if (isString(newSample)) return newSample;
try { return JSON.stringify(newSample, null, indnt); } catch (ex) { return newSample.toString(); } }
const info = JSON.stringify({ name: 'anran758', avatar: 'https://xxx', detail: JSON.stringify({ desc: 'some description', level: 2, }) }) const data = formatJSONIndnt(info); console.log(data);
|
输入
上文讲了如何将数据输出至页面,以及扩展格式化功能的示例。接下来讲解输入方面的应用。
当用户从别的地方复制数据想粘贴至输入框时,可以在输入框上设置监控事件,触发事件后尝试帮用户格式化数据,示例代码如下:
1 2 3 4
| <div class="container"> <pre class="preview pre"></pre> <textarea class="textarea"></textarea> </div>
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| const info = JSON.stringify({ name: 'anran758', avatar: 'https://xxx', detail: JSON.stringify({ desc: 'some description', level: 2, }) }) const data = formatJSONIndnt(info);
const textarea = document.querySelector('.textarea'); const preview = document.querySelector('.pre');
preview.innerHTML = data; textarea.addEventListener('paste', (e) => { e.preventDefault(); const value = (e.clipboardData || window.clipboardData).getData('text');
e.target.value = formatJSONIndnt(value, 2); })
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
| body { display: flex; margin: 0; justify-content: center; align-items: center; padding: 0 10px; box-sizing: border-box; min-height: 100vh; }
.container { display: flex; width: 100%; }
.preview { flex: 1; margin-bottom: 20px; padding: 20px; background: #f5fcff; border: 1px solid #d3eeff; border-radius: 3px; margin: 0; }
.textarea { flex: 1; margin-left: 20px; padding: 10px; font-family: 'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, Courier, monospace; }
.preview + .preview { margin-left: 10px; }
|
参考资料