面试题总结(HTML)

123 阅读13分钟

一、HTML考题

1. 前端页面有哪三层构成,分别是什么?作用是什么?
前端页面主要由三层构成,分别是结构层、表示层和行为层。这三层各自的作用如下:

1. HTML(HyperText Markup Language):结构层,它是网页的基础,用于定义网页的内容结构,如标题、段落、列表、图像等元素。HTML标签告诉浏览器如何组织和呈现这些内容。

2. CSS(Cascading Style Sheets):样式层,CSS负责为HTML元素添加样式,如颜色、字体、布局、间距等,使得内容具有视觉吸引力并适应不同的设备和屏幕尺寸。通过媒体查询,可以实现响应式设计。

3. JavaScript(JS):行为层,JavaScript是动态交互的核心,它使网页能够响应用户的操作,执行复杂的逻辑,比如表单验证、动画效果、数据绑定等。现代前端开发还可能用到框架和库(如React、Vue或Angular)来简化开发过程。
2. 行内元素有哪些?块级元素有哪些? 空(void)元素有哪些?
1. 行内元素(Inline Elements)通常是文本内容的一部分,它们不会影响布局,只占据内容所在的行宽度。例如:
<span>、<a>、<img>、<b>、<strong>、<em> 等。

2. 块级元素(Block Elements)占据一行,形成独立的块,可以设置宽高,影响布局。例如:
<div>、<p>、<h1>~<h6>、<ul>、<ol>、<li>、<table>、<form> 等。

3. 行内块元素 <button> <input> 不占一行,只占自身可见的宽高,可设置宽高

4. 空(void)元素,也称为无内容元素,它们不包含任何内容,但可以有属性。它们通常用于定义结构,不会影响布局。例子有:
<hr>(水平线)、<br>(换行)、<img>(图片,当src为空时)、<input type="hidden"> 等。

元素之间的转换问题:
display: inline;  把某元素转换成了行内元素         ===>不独占一行的,并且不能设置宽高
display: inline-block; 	把某元素转换成了行内块元素 ===>不独占一行的,可以设置宽高
display: block; 把某元素转换成了块元素	        ===>独占一行,并且可以设置宽高
3. 怎么解决2个行内块元素中间的空白

导致原因:元素被当成行内元素排版的时候,元素之间的空白符(空格、回车换行等)都会被浏览器处理,根据white-space的处理方式(默认是normal,合并多余空白),原来HTML代码中的回车换行被转成一个空白符,所以元素之间就出现了空隙。

方案一:手动取消空格和换行:

<ul>
	<li>1</li><li>2</li><li>3</li><li>4</li>
</ul>

非常不推荐,原因:
1. 部分编译器可能会代码整理的时候会强制换行
2. 代码比较臃肿,美观度不行,一点不优雅
方案二:父元素设置font-size:0px(推荐)

<style type="text/css">
	*{margin: 0;padding: 0;}
	ul{
		list-style: none;
		font-size: 0;
	}
	ul li{
		display: inline-block;
		width: 100px;
		height: 100px;
		background: red;
		font-size: 18px;
	}
</style>
<ul>
	<li>1</li>
	<li>2</li>
	<li>3</li>
	<li>4</li>
</ul>

注意:如果子盒子有文本,需要单独设置font-size
4. 页面导入样式时,使用link和@import有什么区别?
区别一:加载方式和顺序
	link标签会先加载(多个 <link> 标签同时引入,加载顺序属于并列加载)
	@import会在当前样式表加载完成后再加载引入外部样式表(@import 规则必须写在样式表的最前面,加载顺序受到 CSS 文件中 @import 规则的顺序决定,属于串行加载)
	加载顺序区别:页面被加载时,link 会同时被加载,而 @import 引用的 CSS 会等到页面被加载完再加载。
	
区别二:从属关系区别
        link 属于 HTML 标签,而 @import 是 CSS 提供的。
	
区别三:兼容性区别
        link 没有兼容性问题,@import 不兼容 ie5 以下。
区别四:使用 dom控制样式时的差别:
        当使用 javascript 控制 dom 去改变样式的时候,只能使用 link 标签,因为@import 不是 dom 可以控制的
5. title与h1的区别、b与strong的区别、i与em的区别?
title与h1的区别:

定义:
	title:概括了网站信息,可以告诉搜索引擎或者用户关于这个网站的内容主题是什么
	h1:文章主题内容,告诉蜘蛛我们的网站内容最主要是什么
区别:
	title他是显示在网页标题上、h1是显示在网页内容上
	title比h1更加的重要 (title > h1 ) ==》对于seo的了解
场景:
	网站的logo都是用h1标签包裹的	
bstrong的区别:

定义:
	b:标签用于为文本设置粗体样式,但它只是简单地表示文本应该呈现为粗体,并不带有语义强调的意义。
	strong:标签用于表示文本的强调语义,默认会呈现为粗体,同时也向屏幕阅读器和搜索引擎表明这段文字的重要性。
区别:
	b标签只有加粗的样式,没有实际含义。
	strong表示标签内字符比较重要,用以强调的。
	
题外话:为了符合css3的规范,b尽量少用,改用strong就行了。
iem的区别:

定义:
	i:标签用于表示文本的斜体样式,但它只是简单地表示文本应该呈现为斜体,并不带有语义强调的意义。
	em:标签用于表示文本的强调语义,通常会呈现为斜体,同时也向屏幕阅读器和搜索引擎表明这段文字的强调重要性。
区别:
	i只是一个倾斜标签,没有实际含义。
	em表示标签内字符重要,用以强调的。
场景:
	i更多的用在字体图标,em术语上(医药,生物)。
6. img标签的title和alt有什么区别?
区别一:表现形式不同
	title : 鼠标移入到图片展示文字值
	alt   : 图片无法显示时展示文字值

区别二:SEO层面 ( alt > title )
	在seo的层面上,蜘蛛抓取不到图片的内容,所以前端在写img标签的时候为了增加seo效果要加入alt属性来描述这张图是什么内容或者关键词。
7. png、jpg、gif 这些图片格式解释一下,分别什么时候用?
一、png:
	特点:png格式支持无损压缩,同样尺寸情况下,体积要比jpg/jpeg的大。
	适用场景:png格式适合保存,需要保持高质量细节和透明背景的图像,如图标、线条图、文字、Logo 等。
	
二、jpg:
	特点:jpg格式支持有损压缩。
	适用场景:jpg式适合保存照片、图像和艺术作品等真实场景图像,常用于网站上发布的照片、背景图等。

三、gif:
	特点:gif格式支持无损压缩和动画功能,能够保存多帧图像并以动画形式显示。
	适用场景:gif格式适合保存简单的动画、图标、表情包等图像。适用于网页上的小型动画、简单图标等。

四、webp:
	特点:webp是一种由 Google 开发的图像格式,支持有损压缩和无损压缩,通常比 PNG 和 JPG 文件大小更小,同时保持较高的图像质量。它还支持动画和透明度。
	适用场景:webp格式适合用于网站优化和速度提升,可以帮助减小图像文件大小,提高网页加载速度。特别适合用于要求高速加载和移动设备访问的网页,可以有效减少网页加载时间。

五、总结:
	PNG 适合保存高质量细节和透明背景的图像
	JPG 适合保存照片和真实场景图像;
	GIF 适合保存简单的动画和图标;
	WebP 适合用于网站优化和速度提升,特别适合要求高速加载和移动设备访问的网页(存在兼容性)。
8. iframe的优点和缺点?
是什么:用于嵌入另一个 HTML 文档或外部资源(如网页、视频、地图等)到当前页面中。
优点:

1.嵌入外部内容: <iframe> 允许你在一个网页中嵌入来自不同源或服务器的内容,这有助于创建丰富多彩的页面。
2.独立性: 内嵌的内容在 <iframe> 中运行,与主页面相互隔离,这意味着它不会受到主页面的影响,保持了独立性。
3.简便性: 使用 <iframe> 非常简单,只需提供要嵌入的资源的 URL 或相对路径即可。
缺点:

1.性能问题: 如果滥用 <iframe>,在同一个页面中加载多个 <iframe> 可能会导致性能问题,因为每个 <iframe> 都需要单独加载资源。
2.可访问性问题: 内嵌内容可能导致可访问性问题,因为屏幕阅读器等辅助技术可能无法正确解释和浏览 <iframe> 内的内容。
3.安全性风险: 如果未谨慎处理来自不受信任源的内容,可能会存在安全风险,例如点击劫持(clickjacking)攻击。

防止自己的网站被别人 iframe 嵌套

if (top != self) {
    top.location = self.location;
}
//这段代码会检测页面是否被嵌套,如果是,则将页面重定向到自身。
9. 对语义化标签的理解?

通俗的来讲就是从代码上来展示页面的结构,而不是从最终视觉上来展示结构。

  • 各个浏览器都自带的有相应标签的默认样式,我们为了方便在没有设定样式的情况下友好的展示页面,需要使用语义化标签。
  • 良好的语义化代码可以直接从代码上就能着出来那一块到底是要表达什么内容。有利于构建清晰的结构,有利于团队的开发、维护
  • 而且页面不止是给人看的,标签语义化有助于构架良好的HTML结构,有利于搜索引擎的建立索引、抓取。有利于不同设备的解析 (屏幕阅读器,盲人阅读器等)。
10.渐进增强和优雅降级?
  • 渐进增强主要针对低版本的浏览器进行页面重构,保证基本的功能情况下,再针对高版本现代浏览器进行效果,交互等方面的改进和追加功能,以达到更好的用户体验。渐进增强是从一个非常基础的,能够起作用的版本开始的,并在此基础上不断扩充,以适应未来环境的需要;
  • 优强降级,一开始始就构建完善的功能,然后再针对低版本的浏选器进行兼容;优雅降级是从复杂的现状开始的,并试图减少用户体验的供给;
  • 优雅降级(功能衰竭)意味着往回看,而渐进增强则意味着往前看,同时保证其根基处于安全地带。
11. h5新特性?
  1. 新增语义化标签:nav、header、footer、aside、section、article·
  2. 音频、视频标签:audio、video
  3. 数据存储:localStorage、sessionStorage
  4. canvas(画布)、Geolocation(地理定位)、websocket(通信协议)·
  5. input标签新增属性:placeholder、autocomplete、autofocus、required
  6. history API 0 go、forward、 back、 pushstate
12. 本地存储考题
12.1 localStorage、sessionStorage、cookie的区别

localStorage、sessionStorage 和 cookie 都是Web存储技术,用于在用户的浏览器中存储数据。

  1. 存储容量

    • localStorage: 通常可以存储 5MB 到 10MB 的数据,具体取决于浏览器。
    • sessionStorage: 通常与 localStorage 相同,也可以存储 5MB 到 10MB 的数据。
    • cookie: 每个 cookie 的大小限制是 4KB,且每个域名下的 cookie 数量通常限制在 20 个左右。
  2. 生命周期

    • localStorage: 数据存储是持久的,除非显式删除,否则数据会一直保存在用户的浏览器中。
    • sessionStorage: 数据仅在当前会话中有效,窗口或标签页关闭后,数据会被清除。
    • cookie: 通过设置 expires 属性,可以定义 cookie 的过期时间,过期后会被删除;如果没有设置,cookie 会在会话结束时过期(类似于 sessionStorage)。
  3. 作用域

    • localStorage: 数据在同一源(同一协议、主机和端口)下的所有页面中共享。
    • sessionStorage: 数据仅在创建该会话的窗口或标签页中可用,不同窗口或标签页之间的数据不共享。
    • cookie: cookie 在同一域名下的所有页面中共享,因此可以在不同的页面之间传递。
  4. 数据格式

    • localStorage 和 sessionStorage: 存储的数据都是以键值对的形式存在,值是字符串类型,可以通过 JSON.stringify() 和 JSON.parse() 来存储和读取对象。
    • cookie: 数据以字符串形式存储,通常需要手动进行编码和解码(例如使用 encodeURIComponent 和 decodeURIComponent)。
  5. 性能

    • localStorage 和 sessionStorage: 相对较快,适合于存储大量数据。
    • cookie: 每次请求都会自动发送到服务器,可能导致性能问题,尤其是存储较大的数据时。
  6. 安全性

    • localStorage 和 sessionStorage: 数据不会自动发送到服务器,较为安全,适合存储不敏感的数据。
    • cookie: 可以设置 HttpOnly 属性以防止 JavaScript 访问,增加安全性;还可以设置 Secure 属性,只在 HTTPS 连接中发送。
12.2 localStorage存储超过最大值怎么办?

方案一:lz-string压缩库(通过压缩的形式)

    import LZString from 'lz-string'

    写入:
    localStorage.setItem('data', LZString.compress('Hello'));

    读取:
    LZString.decompress(localStorage.getItem('data'));

方案二:indexedDB(通过 localforage 库操作 indexedDB 存储数据)

    import localForage from 'localforage';
    const firstIndexedDB = localForage.createInstance({
    	name: "Forage",// 数据库名称
    	storeName: "forage_store_name"// 表名称
    });

    const userList = [
      {name:'张三'},{name:'李四'}
    ]

    // 存储
    firstIndexedDB.setItem('userList', userList).then(val => {
    	val // 数组数据 [{...}, {...}, {...}]
    }).catch(err => {
    	console.log(err);
    });

    // 获取
    firstIndexedDB.getItem('userList').then(val => {
    	val // 数组数据 [{...}, {...}, {...}]
    }).catch(err => {
    	console.log(err);
    });
13. 你用过哪些 HTML 5 标签?
  1. 语义化标签 nav,header,footer,aside,section,article,audio,video

  2. canvas

    • 给视频做水印
      <div style="display: flex;flex-wrap: nowrap;flex-direction: row;">
         <video src="./child.mp4" hidden></video>
         <canvas id="c1" width="600" height="900">
           请下载最新版chrome浏览器 <a href="https://www.google.cn/intl/zh-CN/chrome/">去下载</a>
         </canvas>
       </div>
       <button id="btn">播放/暂停</button>
       <script>
         const c1 = document.querySelector('#c1')
         if (!c1.getContext) {
           alert('请下载最新版chrome浏览器')
         }
         const ctx = c1.getContext('2d')
    
         var video = document.querySelector('video')
         // 获取按钮
         let btn = document.querySelector('#btn')
         btn.onclick = function () {
           if (video.paused) {  // 使用paused来判断视频是否正在播放
             video.play()
             render()
           } else {
             video.pause()
           }
         }
         // logo图片对象
         let img = new Image()
         img.src = './image.png'
         // 逐帧渲染
         function render() {
           ctx.drawImage(video, 0, 0, 600, 900)
           // 添加水印 logo 图片 在右下角
           // 参数1为图片对象
           // 参数2为将图片渲染到画布的水平位置
           // 参数3为将图片渲染到画布的垂直位置
           // 参数4为图片的宽度
           // 参数5为图片的高度
           ctx.drawImage(img, 500, 850, 100, 80)
           requestAnimationFrame(render)
         }
       </script>
    
    • 自定义指令添加水印
     // 注册水印自定义指令
     import Vue from 'vue'
     Vue.directive('watermark', {
       mounted(el, binding) {
         createWatermark(el, binding.value);
       },
       updated(el, binding) {
         createWatermark(el, binding.value);
       }
     });
    
     // 创建水印函数
     function createWatermark(el, config) {
       // 移除旧的水印
       const oldWatermark = el.querySelector('.vue-watermark');
       if (oldWatermark) oldWatermark.remove();
    
       // 创建水印画布
       const canvas = document.createElement('canvas');
       canvas.width = 200;
       canvas.height = 150;
       const ctx = canvas.getContext('2d');
    
       // 绘制水印
       ctx.font = `${config.fontSize}px Arial`;
       ctx.fillStyle = config.color;
       ctx.globalAlpha = config.opacity;
       ctx.rotate(config.rotate * Math.PI / 180);
       ctx.fillText(config.text, 10, 75);
    
       // 创建水印元素
       const watermark = document.createElement('div');
       watermark.className = 'vue-watermark';
       watermark.style.backgroundImage = `url(${canvas.toDataURL()})`; // 将 canvas 画布内容转换为一个包含图片数据的 Data URL(base64 编码的字符串)
       watermark.style.position = 'absolute';
       watermark.style.top = '0';
       watermark.style.left = '0';
       watermark.style.width = '100%';
       watermark.style.height = '100%';
       watermark.style.pointerEvents = 'none'; // 禁用元素对鼠标/指针事件的响应,让鼠标操作"穿透"该元素
       watermark.style.zIndex = '9999';
    
       el.style.position = 'relative';
       el.appendChild(watermark);
    
       // 防删除保护
       protectWatermark(el, watermark, config);
     }
    
     // 防删除保护
     function protectWatermark(el, watermark, config) {
       const observer = new MutationObserver((mutations) => {
         mutations.forEach((mutation) => {
           if (mutation.removedNodes.length) {
             mutation.removedNodes.forEach((node) => {
               if (node === watermark || node?.contains(watermark)) {
                 createWatermark(el, config);
               }
             });
           }
         });
       });
       // 监听子节点变化
       observer.observe(el, {
         childList: true,
         subtree: true
       });
     }
    
  3. websocket

  • 实例化 new Websocket('地址')
    this.socket = new Websocket('地址')
    // 地址
    'wss://' + window.location.host + process.env.VUE_APP_SOCKET_API + token + 其他约定参数
    
  • 连接建立 onopen
    this.socket.onopen = function() {
      console.log('socket建立')
    }
    
  • 连接维护(心跳/重连)
    this.socketKey = setInterval(() => {
      if (this.socket && this.socket.readyState === 1) { // 检查连接是否存活 0: 正在连接 1:连接已建立,可以通信 2: 正在关闭连接 3: 连接已关闭或无法打开
        this.socket.send(JSON.parse(sessionStorage.getItem('operatorInfo')).accountId)
      }
    }, 30000) 
    
  • 数据传输onmessage
    this.socket.onmessage = async data => {
        data = await JSON.parse(data.data)
        // 推送消息
        // 消息撤回
        // 更新会话列表和未读消息计数
        // 处理消息状态
    }
    
  • 连接关闭onclose
    this.socket.onclose = () => {
      clearInterval(this.socketKey) // 清除心跳
    }