让 HTML 网页打印自己的源代码 - HTML的 Quine 程序实现

简介: Quine 程序是指一个能够输出自身代码的计算机程序。在这篇文章中,我们将用HTML代码来实现一个Quine程序。我们把这个任务分成两步,首先让`<head>`标签中的内容显示出来,然后显示元素的标签

Quine 程序是指一个能够输出自身代码的计算机程序。Quine 程序的实现是计算机科学中一个有趣的问题,不同语言中有不同的实现方式,常见的技巧包括代码注入、字符串插值或反射等。实现 Quine 程序可以帮助我们对平常所使用的编程语言产生更深入的理解。

在这篇文章中,我们将用HTML代码来实现一个Quine程序。严格意义上讲,HTML并不是一门编程语言,但我们同样可以让一个HTML网页打印出自己的全部源代码。我们把这个任务分成两步,首先让<head>标签中的内容显示出来,然后让所有的html元素都显示出自己的标签。

1. head 标签中的内容打印

我们首先写一段简单的HTML,其中包括了两个<p>段落和一个<a>标签。

<html>
<head>
  <title>HTML Quine</title>
</head>
<body>
  <p>这个网页显示了自己的源代码</p>
  <p>灵感来源于
    <a href="https://www.youtube.com/watch?v=6avJHaC3C2U">这个讲座</a>
  </p>
</body>
</html>

运行页面效果如图所示:

frame_chrome_mac_dark (32).png

现在我们来思考一下:通常网页中展示的都是<body>标签中的内容,要显示网页的所有源代码意味着<head>标签中的内容也要被展示出来,这要怎么办呢?

大家回想一下浏览器渲染页面的过程,第一步叫做“根据HTML代码构建DOM树”,也就是说其实<head>标签的内容也是在DOM树中的,我们不妨打开浏览器控制台验证一下这一点。

image.png

那为什么<head>标签的内容不会被展示出来呢?我们接着顺着浏览器引擎的渲染过程往下走,接下来它会解析页面的CSS文件,我们这个网页没有任何自定义的CSS,但是浏览器本身是有自带的CSS的,这个叫做 user agent stylesheet,是浏览器给我们的网页提供的一套默认的CSS。不同浏览器的这套样式会有所不同,例如 chrome 就会有一个默认的网页内边距,所以我们才会需要 reset-css 这样的解决方案来磨平这些默认样式的差异。

而我们的 <head> 标签的内容没有被显示出来其实也正是这个 user agent stylesheet 搞的鬼。在控制台中选中 <head> 标签,我们可以通过右侧(或者下方)的 styles 面板来看浏览器最终计算得出的该元素的样式规则。

image.png

可以看到正是user agent stylesheet让我们的head标签display: none,所以才啥也看不见。那如果我们把它改成 display: block 呢?(点击上面的element.style那一栏可以添加自定义的样式,这也是一个 debug 的好方法)

image.png

还是啥也没有!但这是因为<title>也被设置为display: none了。我们再把<title>元素也改为display: block试试。

image.png

真的出现了!那么我们只要把让<head>中的所有元素显示出来的样式代码添加到HTML中就可以完成第一步了。为了方便,我们直接通过通配符来让所有的元素都display: block即可。同时,为了让打印出来的源代码更像代码,我们还设置了一个等宽字体。

<style>
    * {
    
     display: block; font-family: monospace; }
</style>

2. 打印元素标签和属性

现在我们距离成功打印网页的源代码只差了标签文本的显示了。HTML的标签起到了标记文本的作用,并不会作为网页本身的文本呈现,所以要把它们显示出来只能自己手动添加了。我们要做的就是想办法在网页中的每一个元素前后的显示它们的标签文本。emm...在元素的前后添加东西,这不就是beforeafter伪元素吗!以 <p> 标签的显示为例:

p::before {
   
    content: '<p>'; } p::after {
   
    content: '</p>'; }

frame_chrome_mac_dark (37).png
可以看到两个段落的 <p> 标签都显示出来了。同理,我们只需要枚举我们的网页中包含的所有标签就可以把完整的网页源代码打印出来了。

html::before {
   
    content: "<html>"; }
html::after {
   
    content: "</html>"; }

head::before {
   
    content: "<head>"; }
head::after {
   
    content: "</head>"; }

title::before {
   
    content: "<title>"; }
title::after {
   
    content: "<\title>"; }

style::before {
   
    content: "<style>"; }
style::after {
   
    content: "<\/style>"; }

body::before {
   
    content: "<body>"; }
body::after {
   
    content: "</body>"; }

p::before {
   
    content: "<p>"; }
p::after {
   
    content: "</p>"; }

a::before {
   
    content: "<a>" }
a::after {
   
    content: "</a>" }

frame_chrome_mac_dark (33).png

可以看到我们已经把源代码成功显示出来了,但还有几个地方不太符合预期。

首先是<a>标签的href属性没有显示出来,我们可以通过attr函数来获取href属性的内容,再把它拼接到a标签的before文本中。

a::before {
   
    content: "<a href='" attr(href)  "'>"; }

image.png

第二是<style>的内容被挤到了一行里面,没有像我们输入时那样格式化地展示。这是因为HTML解析时会默认忽略源代码中的换行符和空格,我们可以通过white-space: pre-wrap这条规则来让它保留源代码中的空白。

style {
   
    white-space: pre-wrap; }

第三是<body>标签的内容和<head>标签对的不是很齐,还记得我刚刚说过chrome会有一个默认的网页内边距吗,这就是它干的好事!因为正常情况下HTML网页显示的都是<body>标签的内容,所以这个默认的margin就是加在<body>标签身上的,我们把它清除一下就行了。

body {
   
    margin: 0; }

此时的页面效果如图所示:
frame_chrome_mac_dark (35).png

最后还有一个问题是<html>的闭合标签不知所踪,其实是因为它被放到了页面的底部,要滚动一下才能看到。我们可以通过把<html>元素的高度减小来解决这个问题。同时为了让页面更美观一点,我们再给它加上一个16px的内边距。

html {
   
    margin: 16px; height: 1%; }

frame_chrome_mac_dark (36).png

大功告成!我们写的这个HTML网页完完整整地打印出了自己的源代码,并且也保留了原有的代码缩进和换行。最终完整的代码如下:

<html>
<head>
  <title>HTML Quine</title>
  <style>
    * {
    
     display: block; font-family: monospace; }

    html {
    
     margin: 16px; height: 1%; }
    html::before {
    
     content: "<html>"; }
    html::after {
    
     content: "</html>"; }

    head::before {
    
     content: "<head>"; }
    head::after {
    
     content: "</head>"; }

    title::before {
    
     content: "<title>"; }
    title::after {
    
     content: "<\title>"; }

    style {
    
     white-space: pre-wrap; }
    style::before {
    
     content: "<style>"; }
    style::after {
    
     content: "<\/style>"; }

    body {
    
     margin: 0; }
    body::before {
    
     content: "<body>"; }
    body::after {
    
     content: "</body>"; }

    p::before {
    
     content: "<p>"; }
    p::after {
    
     content: "</p>"; }

    a::before {
    
     content: "<a href='" attr(href)  "'>"; }
    a::after {
    
     content: "</a>" }
  </style>
</head>
<body>
  <p>这个网页显示了自己的源代码</p>
  <p>灵感来源于
    <a href="https://www.youtube.com/watch?v=6avJHaC3C2U">这个讲座</a>
  </p>
</body>
</html>

总结

  1. <head>标签的内容没有显示出来是因为浏览器自定义的样式表隐藏了其内容,我们可以自定义样式覆盖浏览器定义的规则,就可以让其显示出来。
  2. 元素标签的显示可以通过伪元素实现,元素属性的内容可以通过attr函数获取并展示。

本文作者wzkMaster,如果对你有帮助的话欢迎点赞收藏~

相关文章
|
2月前
|
移动开发 前端开发 JavaScript
编程笔记 html5&css&js 005 网页上都有哪内容、形式和操作
编程笔记 html5&css&js 005 网页上都有哪内容、形式和操作
|
4月前
|
移动开发 前端开发 数据安全/隐私保护
HTML的基本语法以及如何使用HTML来创建网页
HTML的基本语法以及如何使用HTML来创建网页
81 0
|
4月前
|
存储 缓存 前端开发
探索HTML的黑科技:让你的网页变得无与伦比!(二)
探索HTML的黑科技:让你的网页变得无与伦比!
|
4月前
|
前端开发 JavaScript
百度搜索:蓝易云【用JavaScript和HTML实现一个精美的计算器网页】
该计算器网页使用HTML定义了页面结构,CSS样式使其具有精美的外观,而JavaScript脚本实现了计算器的逻辑。用户可以通过按钮输入数字和操作符,并通过“=”按钮来进行计算,计算结果会显示在文本框中。
42 6
|
11天前
|
数据采集 XML 数据挖掘
使用Python打造爬虫程序之HTML解析大揭秘:轻松提取网页数据
【4月更文挑战第19天】本文介绍了HTML解析在爬虫技术中的重要性,并通过Python的BeautifulSoup库展示了如何解析和提取数据。文章涵盖了HTML文档结构、使用BeautifulSoup的基本方法,如`find_all()`、选择器(标签、类、ID选择器)以及提取文本、属性和链接。此外,还讨论了遍历和处理嵌套元素的技巧。
|
22天前
|
JavaScript C#
C#winForm程序与html JS交互调用
C#winForm程序与html JS交互调用
|
2月前
|
搜索推荐 前端开发 JavaScript
打造个性化的个人网页:从HTML到个人品牌
打造个性化的个人网页:从HTML到个人品牌
22 0
|
2月前
|
JavaScript 前端开发
HTML网页自动刷新_kaic
HTML网页自动刷新_kaic
|
2月前
|
JavaScript
【HTML特效程序】① 给女神表白的程序(让女神看科技烟花),输入名字自动生成表白二维码
【HTML特效程序】① 给女神表白的程序(让女神看科技烟花),输入名字自动生成表白二维码
18 0
|
2月前
|
前端开发 JavaScript 开发者
编程笔记 html5&css&js 014 网页布局框架
编程笔记 html5&css&js 014 网页布局框架