跳到主要内容

79 篇博文 含有标签「前端」

前端开发技术文章

查看所有标签

Web 开发下 MVC 流程

· 阅读需 4 分钟
素明诚
Full stack development

流程图​

ef754b81052e9cd5cc26059ecbf3b353### 前端发起请求,传递必要的参数(如用户 ID)

前端通过浏览器、移动客户端或其他客户端发起 HTTP 请求,通常是 RESTful API 调用(GET、POST等)。例如,前端可能请求一个特定 ID 的用户信息。 请求中可能携带查询参数(如分页参数、排序条件、筛选条件等)或路径参数(如用户 ID),具体取决于 API 的设计。

路由层解析请求并将其转发给相应的控制器方法​

**路由层(Router)**负责解析 HTTP 请求并将其转发给相应的控制器方法。路由会根据请求的方法(GET、POST 等)和路径(如/api/user/{id})来匹配相应的控制器。 在现代 Web 框架(如 Gin、Express、Spring 等)中,路由通常会独立于控制器进行配置,路由管理器会根据 URL 匹配到相应的控制器方法。在某些框架中,路由层和控制器层可能是分开的。

控制器从请求中提取参数,并调用服务层来获取数据​

**控制器层(Controller)**负责接收请求并提取必要的参数(如用户 ID、查询条件等)。控制器的主要职责是调度请求,决定需要调用哪个服务层的功能,执行相应的业务操作。 控制器层本身并不包含业务逻辑,所有的业务逻辑应交给服务层处理。控制器将接收到的参数传递给服务层,以获取或处理数据。

服务层处理业务逻辑,并调用数据库(通过模型)获取数据​

**服务层(Service)专注于处理具体的业务逻辑。在这一步,服务层会执行参数验证、数据计算或其他必要的处理逻辑。 服务层通过模型层(Model)**来访问数据库或其他持久化存储,获取、插入或更新数据。模型层通常通过 ORM(如 Gorm、Sequelize 等)与数据库进行交互。 服务层还可能包括缓存的读取、日志记录、外部 API 的调用等跨域逻辑。

数据从数据库返回后,服务层将其返回给控制器​

在完成数据库操作后,服务层将返回的数据(通常是对象或结构化数据)传递给控制器层。 服务层通常返回的格式可以是一个模型对象,也可能是经过一定处理后的数据,取决于具体的业务需求。

控制器使用 DTO 将从数据库获取的数据转换为前端需要的格式​

控制器层将从服务层获得的模型数据(Model)转换为前端所需的格式,通常是通过DTO(数据传输对象)来实现。 DTO 用于将数据格式化为 API 所需的标准结构,并去除不必要的字段或敏感数据(如密码、内部 ID 等)。 在一些复杂场景下,控制器也可能使用ViewModel,特别是当数据的展示层要求较高时,ViewModel 更多地关注展示层的需要,可能包含计算字段或格式化的结果。

控制器通过 HTTP 响应将 DTO 数据返回给前端​

控制器将经过 DTO 转换后的数据以HTTP 响应的形式返回给前端。响应通常以JSON或XML格式发送,具体取决于前后端约定。

前端接收到数据后进行处理并展示给用户​

前端接收到数据后,通过 JavaScript 等前端技术进行展示。通常,前端会基于 API 返回的 JSON 数据动态更新页面内容,使用框架如React、Vue、Angular等来处理数据绑定和 UI 渲染。 前端还可能进行一些额外的数据处理,例如表单验证、UI 更新或交互。在复杂的前端应用中,状态管理(如 Redux、Vuex 等)可能会涉及,帮助集中管理应用的状态。

iframe 在同源和跨域的通信方式

· 阅读需 4 分钟
素明诚
Full stack development

同源​

父页面和 iframe 可以直接访问对方的 DOM 和 JavaScript 对象。

这种方式简单高效,但需要注意代码的安全性和健壮性,避免产生错误或安全漏洞。

跨域​

由于同源策略的限制,需要使用 window.postMessage() 方法进行通信。

同源情况下的通信​

直接访问 iframe 的内容​

从父页面访问 iframe

父页面可以通过 iframe 元素的 contentWindow 或 contentDocument 属性获取 iframe 的 window 对象或 document 对象,然后直接访问其 DOM 或 JavaScript 变量。

从 iframe 访问父页面​

iframe 可以通过 window.parent 访问父页面的 window 对象。

示例​

父页面(parent.html)

<!DOCTYPE html>
<html>
<head>
<title>父页面</title>
</head>
<body>
<h1>父页面</h1>
<iframe id="myIframe" src="iframe.html"></iframe>
​
<script>
// 等待 iframe 加载完成
document.getElementById('myIframe').onload = function() {
// 获取 iframe 的 window 对象
var iframeWindow = document.getElementById('myIframe').contentWindow;
​
// 调用 iframe 中的函数
iframeWindow.showMessageFromIframe();
​
// 访问 iframe 中的变量
console.log('从 iframe 获取的变量:', iframeWindow.iframeVariable);
​
// 修改 iframe 中的 DOM 元素
var iframeDocument = iframeWindow.document;
iframeDocument.getElementById('iframeElement').innerText = '父页面修改了 iframe 的内容';
​
// 从 iframe 获取输入框的值
var inputValue = iframeDocument.getElementById('iframeInput').value;
console.log('从 iframe 获取的输入框值:', inputValue);
};
​
// 定义供 iframe 调用的函数
function showMessageFromParent() {
alert('这是父页面的消息!');
}
</script>
</body>
</html>

iframe 页面(iframe.html)

<!DOCTYPE html>
<html>
<head>
<title>iframe 页面</title>
</head>
<body>
<h2>iframe 页面</h2>
<p id="iframeElement">这是 iframe 中的段落。</p>
<input type="text" id="iframeInput" value="iframe 输入框的默认值">
​
<script>
// 定义变量
var iframeVariable = '这是 iframe 中的变量';
​
// 定义函数
function showMessageFromIframe() {
alert('这是 iframe 中的消息!');
}
​
// 调用父页面的函数
window.parent.showMessageFromParent();
​
// 访问父页面的 DOM 元素
var parentTitle = window.parent.document.title;
console.log('父页面的标题是:', parentTitle);
​
// 修改父页面的背景颜色
window.parent.document.body.style.backgroundColor = '#f0f8ff';
</script>
</body>
</html>

确保 iframe 已经加载完成,才能访问其内容。


跨域情况下的通信​

当父页面和 iframe 来自不同的域名、协议或端口时,浏览器的同源策略将阻止它们直接访问对方的内容。

使用 postMessage 进行跨域通信​

window.postMessage() 方法允许来自不同源的窗口之间安全地进行通信。

示例​

父页面(parent.html)

<!DOCTYPE html>
<html>
<head>
<title>父页面</title>
</head>
<body>
<h1>父页面</h1>
<iframe id="myIframe" src="https://otherdomain.com/iframe.html"></iframe>
​
<script>
var iframe = document.getElementById('myIframe');
​
// 向 iframe 发送消息
iframe.onload = function() {
var message = { type: 'GREETING', text: '你好,iframe!' };
iframe.contentWindow.postMessage(message, 'https://otherdomain.com');
};
​
// 接收来自 iframe 的消息
window.addEventListener('message', function(event) {
// 验证消息来源
if (event.origin !== 'https://otherdomain.com') {
console.warn('来自未知来源的消息:', event.origin);
return;
}
​
// 处理消息
console.log('收到来自 iframe 的消息:', event.data);
});
</script>
</body>
</html>

iframe 页面(iframe.html)

<!DOCTYPE html>
<html>
<head>
<title>iframe 页面</title>
</head>
<body>
<h2>iframe 页面</h2>
​
<script>
// 接收来自父页面的消息
window.addEventListener('message', function(event) {
// 验证消息来源
if (event.origin !== 'https://parentdomain.com') {
console.warn('来自未知来源的消息:', event.origin);
return;
}
​
// 处理消息
console.log('收到来自父页面的消息:', event.data);
​
// 回复父页面
var reply = { type: 'RESPONSE', text: '你好,父页面!' };
event.source.postMessage(reply, event.origin);
});
</script>
</body>
</html>

在 postMessage 中指定准确的 targetOrigin,不要使用 "*",以防止消息被发送到不受信任的窗口。

最佳实践和安全考虑​

明确的 targetOrigin:始终在 postMessage 中指定准确的目标源,防止数据泄露。

// 不推荐的做法
iframe.contentWindow.postMessage(message, '*'); // 可能导致安全风险
​
// 推荐的做法
iframe.contentWindow.postMessage(message, 'https://otherdomain.com');

验证 event.origin:在接收消息时,验证消息的来源。

window.addEventListener('message', function(event) {
if (event.origin !== 'https://expected-origin.com') {
console.warn('不可信的消息来源,已忽略。');
return;
}
// 处理可信的消息
});

验证消息的数据结构和内容:确保消息的数据格式符合预期,防止被恶意代码利用。

if (typeof event.data !== 'object' || !event.data.type) {
console.warn('消息格式不正确,已忽略。');
return;
}

packagejson 三种类型的依赖

· 阅读需 3 分钟
素明诚
Full stack development

package.json​

{
"dependencies" {
"react" "^18.2.0",
"react-dom" "^18.2.0",
"axios" "^1.2.3"
},
"devDependencies" {
"webpack" "^5.60.0",
"webpack-cli" "^4.9.0",
"babel-loader" "^8.2.3",
"eslint" "^8.0.0",
"style-loader" "^3.3.1",
"css-loader" "^6.7.0"
},
"peerDependencies" {
"react" "^18.0.0"
}
}

dependencies​

dependencies 中列出了项目运行时必需的包。换句话说,这些库是在应用运行时必须存在的。

React 和 React-DOM 是 React 应用的核心库,它们会被安装到 node_modules 中并在应用启动时加载。

Axios 是一个用于发送 HTTP 请求的库,通常用于与后端 API 进行交互,它也属于运行时所需的依赖。

"dependencies" {
"react" "^18.2.0", // React库,应用必须依赖它来构建UI
"react-dom" "^18.2.0", // React-DOM库,用于在DOM中渲染React组件
"axios" "^1.2.3" // Axios,用于发送 HTTP 请求
}

当你运行 npm install 时,dependencies 中列出的所有库都会被安装到项目中。

devDependencies​

devDependencies 中列出了项目在开发阶段需要的工具和库。这些包只会在开发环境中使用,并且不会在生产环境中被包含。

Webpack 是打包工具,用于将代码捆绑成生产环境所需的文件。

Babel-loader 是用来将现代 JavaScript(如 ES6+)转译成兼容性更好的版本,通常配合 Webpack 使用。

ESLint 是一个 JavaScript 代码检查工具,用于保证代码质量。

"devDependencies" {
"webpack" "^5.60.0", // Webpack,用于打包代码
"webpack-cli" "^4.9.0", // Webpack CLI,允许在命令行中使用Webpack
"babel-loader" "^8.2.3", // Babel loader,用于转译JS代码
"eslint" "^8.0.0", // ESLint,用于代码风格和质量检查
"style-loader" "^3.3.1", // Style loader,处理CSS的加载
"css-loader" "^6.7.0" // CSS loader,处理CSS文件
}

这些库的主要作用是为了帮助你在开发过程中构建和优化代码。例如,你使用 Webpack 打包代码,Babel 将 ES6 转译为 ES5,ESLint 确保代码符合规范。而这些工具在生产环境中不需要,因此它们只会被列在 devDependencies 中。

peerDependencies​

peerDependencies 用来指定该库所依赖的外部库的版本范围,但并不直接安装这些库,而是要求使用该库的项目自己安装这些依赖。

假设你在开发一个 React 组件库,这个组件库依赖于 react,但是你并不希望直接将 react 包含在你的组件库中,因为你的用户可能已经安装了 react。这时,你就可以在 peerDependencies 中指定 react 的版本范围。

"peerDependencies" {
"react" "^18.0.0" // 组件库要求项目使用 React 18 或更高版本
}

这样,假设用户已经在他们的项目中安装了 react@18.0.0,那么他们就可以使用你的组件库了。如果用户没有安装正确版本的 react,npm 会给出警告提示。

jsdiff 使用指南

· 阅读需 3 分钟
素明诚
Full stack development

https://github.com/kpdecker/jsdiff 是一个用于计算文本差异的 JavaScript 库。它可以在浏览器和 Node.js 环境中运行,方便地比较两段文本并获取它们之间的差异信息。

本文将介绍如何在前端项目中使用 jsdiff,并通过一些示例来帮助你快速上手。

安装​

你可以通过 pnpm 或 yarn 安装 jsdiff

pnpm install diff
# 或
yarn add diff

在浏览器中,你也可以通过 CDN 引入

<script src="https://unpkg.com/diff/dist/diff.min.js"></script>

基本用法​

jsdiff 提供了多种比较文本的方法,常用的有

  • diffChars(oldStr, newStr[, options])按字符比较
  • diffWords(oldStr, newStr[, options])按单词比较
  • diffLines(oldStr, newStr[, options])按行比较

这些方法返回一个差异对象数组,你可以根据这些对象来展示差异。

示例按字符比较​

const Diff = require('diff');
​
const oldStr = 'Hello World';
const newStr = 'Hello Javascript World';
​
const diff = Diff.diffChars(oldStr, newStr);
​
diff.forEach((part) => {
// 添加部分用绿色显示,删除部分用红色显示,未变部分用灰色显示
const color = part.added ? 'green' :
part.removed ? 'red' : 'grey';
console.log(part.value, color);
});

输出

Hello  grey
Javascript green
World grey

示例按单词比较​

const Diff = require('diff');
​
const oldStr = 'I love programming.';
const newStr = 'We love coding.';
​
const diff = Diff.diffWords(oldStr, newStr);
​
diff.forEach((part) => {
const color = part.added ? 'green' :
part.removed ? 'red' : 'grey';
console.log(part.value, color);
});

输出

We green
I red
love grey
coding. green
programming. red

示例按行比较​

const Diff = require('diff');
​
const oldStr = `Line one
Line two
Line three`;
​
const newStr = `Line one
Line 2
Line three`;
​
const diff = Diff.diffLines(oldStr, newStr);
​
diff.forEach((part) => {
const color = part.added ? 'green' :
part.removed ? 'red' : 'grey';
console.log(part.value, color);
});

输出

Line one
grey
Line two
red
Line 2
green
Line three grey

创建补丁​

jsdiff 可以生成统一格式(unified format)的补丁,方便在版本控制中使用。

示例创建补丁​

const Diff = require('diff');
​
const oldStr = 'Hello World';
const newStr = 'Hello Javascript World';
​
const patch = Diff.createPatch('filename.txt', oldStr, newStr);
​
console.log(patch);

输出

Index: filename.txt
===================================================================
--- filename.txt
+++ filename.txt
@@ -1,1 +1,1 @@
-Hello World
+Hello Javascript World

应用补丁​

你也可以将补丁应用到原始文本上。

示例应用补丁​

const Diff = require('diff');
​
const oldStr = 'Hello World';
const patch = `Index: filename.txt
===================================================================
--- filename.txt
+++ filename.txt
@@ -1,1 +1,1 @@
-Hello World
+Hello Javascript World`;
​
const newStr = Diff.applyPatch(oldStr, patch);
​
console.log(newStr);

输出

Hello Javascript World

比较数组​

除了字符串,jsdiff 还可以比较数组。

示例比较数组​

const Diff = require('diff');
​
const oldArr = ['apple', 'banana', 'cherry'];
const newArr = ['apple', 'blueberry', 'cherry', 'date'];
​
const diff = Diff.diffArrays(oldArr, newArr);
​
diff.forEach((part) => {
const added = part.added ? '+' : '';
const removed = part.removed ? '-' : '';
console.log(`${added}${removed}${part.value}`);
});

输出

apple
-banana
+blueberry
cherry
+date

选项参数​

jsdiff 的比较方法可以接受一个可选的 options 对象,常用的选项有

  • ignoreCase忽略大小写
  • ignoreWhitespace忽略空白字符

示例忽略空白字符​

const Diff = require('diff');
​
const oldStr = 'Hello World';
const newStr = 'Hello World';
​
const diff = Diff.diffWords(oldStr, newStr, { ignoreWhitespace: true });
​
diff.forEach((part) => {
const color = part.added ? 'green' :
part.removed ? 'red' : 'grey';
console.log(part.value, color);
});

输出

Hello World grey

在浏览器中使用​

在浏览器中,你可以直接使用 <script> 标签引入 jsdiff,然后使用全局变量 Diff。

<script src="https://unpkg.com/diff/dist/diff.min.js"></script>
<script>
const oldStr = 'Hello World';
const newStr = 'Hello Javascript World';
​
const diff = Diff.diffWords(oldStr, newStr);
​
diff.forEach((part) => {
const color = part.added ? 'green' :
part.removed ? 'red' : 'grey';
console.log(part.value, color);
});
</script>

原理​

http://www.xmailserver.org/diff2.pdf

解决 Vue 中的事件冒泡问题

· 阅读需 2 分钟
素明诚
Full stack development

在开发 Vue 应用时,经常需要处理事件冒泡问题,特别是在多层嵌套的组件结构中。以下是两种有效的方法来阻止事件冒泡,确保组件行为按预期执行。

使用 Vue 的.stop 修饰符​

Vue 提供了 .stop 修饰符,这是一个简洁且直接的方法来阻止事件冒泡。使用 .stop 修饰符,可以直接在模板的事件绑定上操作,无需修改 JavaScript 逻辑。这种方法非常适合大多数场景,尤其是在简单的父子组件通信中。

实现示例​

在 Vue 模板中,将 .stop 修饰符添加到事件绑定上,如下所示

<button @click.stop="toggleDescription">展开/收起</button>

在这个例子中,当点击按钮时,.stop 修饰符会阻止事件继续向上冒泡至父元素。这样可以避免触发父元素或其他上层元素的事件监听器。

在事件处理函数中调用 event.stopPropagation()​

如果需要更精细的控制,或者想在执行某些条件逻辑后才决定是否阻止冒泡,可以在事件处理函数中调用 event.stopPropagation() 方法。这种方法提供了更高的灵活性,允许开发者根据运行时的条件来动态决定是否阻止事件冒泡。

实现示例​

首先,在方法中接收事件对象,然后调用 stopPropagation() 方法

const toggleDescription = (event) => {
event.stopPropagation();
// 可以在这里添加其他逻辑
isExpanded.value = !isExpanded.value;
}

然后,在模板中传递事件对象到该方法

<button @click="toggleDescription($event)">展开/收起</button>

这种方式允许在函数内部根据具体的逻辑来决定是否停止事件的进一步传播。例如,可以基于用户的权限级别或应用的状态来决定是否阻止冒泡。

什么是浏览器的 Mixed Content 问题

· 阅读需 4 分钟
素明诚
Full stack development

Mixed Content(混合内容)是指在浏览器中访问一个使用 HTTPS(超文本传输安全协议)加载的网页时,该网页包含了一些通过 HTTP(不安全的超文本传输协议)加载的资源。浏览器会将这种情况视为安全风险,因为 HTTP 的资源是不加密的,可能被第三方截获或篡改,从而破坏整个页面的安全性。

为什么 Mixed Content 是问题?​

HTTPS 提供了三大关键安全属性

加密 防止数据在传输过程中被第三方监听。

数据完整性 防止数据在传输过程中被篡改。

身份验证 确保与服务器通信的客户端是合法的。

如果在 HTTPS 页面中加载 HTTP 资源

HTTP 资源的数据是未加密的,可能被中间人攻击(如篡改 JavaScript 脚本、图片、CSS)。

这使得原本安全的页面不再完全可信,破坏了 HTTPS 的初衷!。

Mixed Content 的分类​

根据加载的 HTTP 资源类型,Mixed Content 被分为两类

Passive Mixed Content(被动混合内容)​

这类资源不会直接与页面的其他内容互动。

例如 图片、视频、音频、样式文件(CSS)。

浏览器可能会允许被动混合内容的加载,但会在地址栏或开发者工具中显示警告。

Active Mixed Content(主动混合内容)​

这类资源会与页面的其他部分互动,直接影响页面的安全性。

例如 JavaScript 脚本、内联框架(iframe)、AJAX 请求等。

由于这些资源可能会被劫持和篡改,浏览器会直接阻止主动混合内容的加载。

浏览器如何处理 Mixed Content?​

不同浏览器的处理方式可能略有不同,但大多数现代浏览器(如 Chrome、Firefox、Edge 等)都会采取以下措施

被动混合内容​

浏览器会允许加载这些资源,但会显示安全警告,提示用户当前页面加载了不安全的内容。

主动混合内容​

浏览器会完全阻止加载这些资源,防止不安全的内容影响页面的安全性。

通常会在控制台(Console)中看到错误信息,并且不会加载这些不安全的资源。

Mixed Content 的解决方法​

要解决 Mixed Content 问题,你需要确保页面上所有资源都通过 HTTPS 加载。以下是几种常见的解决方法

确保所有资源使用 HTTPS​

将页面中所有引用的外部资源(如图片、脚本、样式表、iframe 等)都更改为 HTTPS。

如果资源服务器不支持 HTTPS,考虑将资源托管在支持 HTTPS 的服务器上,或联系资源提供方升级其服务。

相对协议使用​

你可以使用相对协议来加载资源(即 // 开头的 URL),例如

<script src="//example.com/script.js"></script>

这种方式会自动使用当前页面的协议来加载资源。如果当前页面是 HTTPS,资源也将通过 HTTPS 加载。

将不支持 HTTPS 的资源代理到 HTTPS​

如果外部资源不支持 HTTPS,可以考虑将请求代理到你的服务器,通过你的服务器以 HTTPS 加载该资源。

检测和修复​

使用浏览器的开发者工具(F12 -> Console 或 Network 标签页)查看哪些资源导致了 Mixed Content 问题。

修复后重新加载页面,确认问题是否解决。

具体的案例分析​

假设你有一个通过 HTTPS 加载的网页,但是其中的图片和 JavaScript 资源是通过 HTTP 加载的。可能出现以下情况

图片资源(被动混合内容)

<img src="http://example.com/image.jpg">

浏览器可能会允许图片加载,但会显示安全警告。

JavaScript 资源(主动混合内容)

<script src="http://example.com/script.js"></script>

浏览器会阻止 JavaScript 文件加载,并在控制台显示错误信息,因为 JavaScript 可能会影响页面的安全性。

浏览器显示的错误提示​

Chrome​

Mixed Content: The page at 'https://example.com' was loaded over HTTPS, but requested an insecure resource 'http://example.com/resource.js'. This request has been blocked; the content must be served over HTTPS.

Firefox​

Blocked loading mixed active content "http://example.com/resource.js"
Loading mixed (insecure) display content "http://example.com/image.jpg" on a secure page

解决 naive-ui 的n-input-number 组件在表单验证失效

· 阅读需 2 分钟
素明诚
Full stack development

问题背景​

我使用的是"naive-ui": "^2.38.1"这个版本的组件库。搜索了一下关于 n-input-number 组件在表单验证中遇到问题的情况,发现这是一个比较常见的问题。很多人都遇到了 n-input-number 的值明明是合法的,但表单验证就是不通过的情况。

问题的原因可能在于 n-input-number 的值是一个数字,而表单验证器默认期望的是一个字符串。

为了解决这个问题,你可以尝试以下方法

自定义验证函数​

在 formRules 中,为 defaultText 字段添加一个自定义验证函数,将数字转换为字符串后再进行验证

defaultText [
{
required true,
message '默认生成字数不能为空',
trigger 'blur',
validator (rule, value) => {
if (!value) {
return new Error('默认生成字数不能为空');
}
return true;
}
}
]

使用 n-input 组件代替 n-input-number​

如果上面的方法不能解决问题,你可以考虑使用 n-input 组件代替 n-input-number,然后在 formRules 中使用正则表达式来验证输入的是否是合法的数字

<n-input
placeholder="请输入生成字数"
v-model="formState.defaultText"
></n-input>
defaultText [
{
required true,
message '默认生成字数不能为空',
trigger 'blur',
pattern /^\d+$/,
message '默认生成字数必须是正整数'
}
]

看issue的时候发现很多人吐槽组件库的校验规则有问题,如果你对组件规则校验有很强的依赖,建议使用其他的组件库~

解决非表单元素无法聚焦问题

· 阅读需 2 分钟
素明诚
Full stack development

通常只有表单元素(如输入框和按钮)以及超链接默认可以聚焦。如果你想让其他元素(如 div, span, p 等)也能够聚焦,你可以使用 HTML 的 tabindex 属性来实现。

tabindex​

可聚焦但不改变 Tab 顺序

使用 tabindex="0" 可以让元素按照文档的自然流程接受键盘焦点,这不会影响原有的 Tab 顺序。

<div tabindex="0">我可以聚焦!</div>

可编程聚焦但不包括在 Tab 顺序中

使用 tabindex="-1" 可以使元素通过脚本调用 .focus() 方法获得焦点,但它不会通过 Tab 键获得焦点。

<div tabindex="-1" id="focusableDiv">点击按钮后我会聚焦!</div>
<button onclick="document.getElementById('focusableDiv').focus()">聚焦到上面的 DIV</button>

聚焦状态样式设置​

你可以为聚焦的元素定义 CSS 样式,以提供视觉反馈。

.focusablehover, .focusablefocus {
outline 2px solid blue; /* 聚焦或悬停时显示蓝色轮廓 */
}

JavaScript 管理聚焦​

使用 JavaScript 监听事件和处理聚焦:

const myElement = document.getElementById('myElement');
myElement.addEventListener('click', function() {
this.focus(); // 点击时聚焦到元素
});

注意事项​

过度使用:虽然 tabindex 很有用,但过度使用(特别是大量使用 tabindex 大于 0 的值)可能会导致键盘导航混乱,不宜滥用。

无障碍:确保使用 tabindex 时不要破坏内容的可访问性。使用语义化的 HTML 标签和角色(role)属性来帮助辅助技术理解元素的意图。

ERR SSL VERSION OR CIPHER MISMATCH 浏览器错误

· 阅读需 1 分钟
素明诚
Full stack development

浏览器报错​

3c0d433cb25597abcf9d8fd57760a079## 错误排查

  • 确认 DNS 记录:确保域名的 DNS 记录(如 A 记录或 CNAME 记录)正确指向了托管网站的服务器。你可以使用 nslookup 或 dig 命令来查看 DNS 解析结果。
  • 检查域名指向:使用 ping 命令或其他网络工具检查域名是否解析到了预期的 IP 地址。
  • SSL/TLS 证书检查:访问网站时检查浏览器的安全信息,确认是否加载了正确的 SSL/TLS 证书。这可以在浏览器的地址栏中点击锁形图标查看。

解决​

经过排查后,我这边的问题是因为腾讯云变更了免费 HTTPS 证书的政策,取消了我的根域名解析。需要在域名解析中添加@记录,指向你的服务器的 IP 地址即可