工作中常用的JavaScript方法整理汇总
1、实现全屏
该方法通过兼容不同浏览器的全屏API(如requestFullScreen
、webkitRequestFullScreen
等),将页面根元素(document.documentElement
)设置为全屏状态,适配主流浏览器(Chrome、Firefox、Safari、Edge)。
javascript
function fullScreen() {
const el = document.documentElement
// 兼容不同浏览器的全屏请求方法
const rfs =
el.requestFullScreen ||
el.webkitRequestFullScreen ||
el.mozRequestFullScreen ||
el.msRequestFullscreen
if (typeof rfs !== "undefined" && rfs) {
rfs.call(el) // 调用全屏方法
}
}
// 调用函数触发全屏
fullScreen()
Demo :
点击按钮触发全屏,可在浏览器中直接测试(需注意部分浏览器需用户主动交互才能触发全屏,如点击事件内调用):
html
<button onclick="fullScreen()">开启全屏</button>
<script>
function fullScreen() {
const el = document.documentElement
const rfs = el.requestFullScreen || el.webkitRequestFullScreen || el.mozRequestFullScreen || el.msRequestFullscreen
if (typeof rfs !== "undefined" && rfs) {
rfs.call(el)
}
}
</script>
2、退出全屏
与"实现全屏"对应,该方法兼容不同浏览器的退出全屏API(如exitFullscreen
、mozCancelFullScreen
等),关闭当前页面的全屏状态,恢复正常视图。
javascript
function exitScreen() {
// 兼容不同浏览器的退出全屏方法
if (document.exitFullscreen) {
document.exitFullscreen()
} else if (document.mozCancelFullScreen) {
document.mozCancelFullScreen()
} else if (document.webkitCancelFullScreen) {
document.webkitCancelFullScreen()
} else if (document.msExitFullscreen) {
document.msExitFullscreen()
}
// 注:原代码中"cfs"未定义,此处保留原逻辑,实际使用时可删除该分支
// if (typeof cfs !== "undefined" && cfs) {
// cfs.call(el)
// }
}
// 调用函数退出全屏
exitScreen()
Demo :
配合"开启全屏"按钮,添加退出全屏功能,需在全屏状态下点击才生效:
html
<button onclick="fullScreen()">开启全屏</button>
<button onclick="exitScreen()">退出全屏</button>
<script>
function fullScreen() {
const el = document.documentElement
const rfs = el.requestFullScreen || el.webkitRequestFullScreen || el.mozRequestFullScreen || el.msRequestFullscreen
if (typeof rfs !== "undefined" && rfs) {
rfs.call(el)
}
}
function exitScreen() {
if (document.exitFullscreen) {
document.exitFullscreen()
} else if (document.mozCancelFullScreen) {
document.mozCancelFullScreen()
} else if (document.webkitCancelFullScreen) {
document.webkitCancelFullScreen()
} else if (document.msExitFullscreen) {
document.msExitFullscreen()
}
}
</script>
3、页面打印
直接调用浏览器原生的window.print()
方法,触发页面打印对话框,可选择打印机、打印范围等配置,适用于需要快速打印页面内容的场景(如订单、报表)。
javascript
// 触发页面打印
window.print()
Demo :
添加打印按钮,点击后弹出浏览器打印窗口,可预览并打印当前页面:
html
<div>
<h1>打印测试内容</h1>
<p>这是一段需要打印的文本</p>
<button onclick="window.print()">打印页面</button>
</div>
4、打印内容样式更改
通过CSS的@media print
媒体查询,自定义打印时的页面样式(如隐藏不需要打印的元素、调整字体/颜色等),实现"屏幕显示"与"打印效果"的差异化。
css
<style>
/* 打印时的专属样式:隐藏class为noprint的元素 */
@media print {
.noprint {
display: none;
}
/* 可选:调整打印内容的字体和颜色 */
.print {
font-size: 16px;
color: #333;
}
}
</style>
<!-- 打印时显示的内容 -->
<div class="print">这部分内容会被打印</div>
<!-- 打印时隐藏的内容(如按钮、导航) -->
<div class="noprint">
<button onclick="window.print()">打印按钮(打印时隐藏)</button>
</div>
Demo :
测试打印效果时,"打印按钮"会被隐藏,仅保留核心内容:
html
<style>
@media print {
.noprint { display: none; }
.print { font-size: 18px; color: #000; }
}
</style>
<div class="print">
<h2>用户订单</h2>
<p>订单号:20240508001</p>
<p>商品:JavaScript教程</p>
</div>
<div class="noprint">
<button onclick="window.print()">打印订单</button>
<button>返回首页</button>
</div>
5、阻止关闭事件
通过监听window.onbeforeunload
事件,在用户刷新页面、关闭标签页或导航离开时,弹出确认提示框(部分浏览器不支持自定义提示文本,会显示默认文案),适用于防止用户误操作导致数据丢失(如表单未提交)。
javascript
// 监听页面卸载前事件
window.onbeforeunload = function() {
// 自定义提示文本(部分浏览器可能忽略,显示默认内容)
return '您确定要离开吗?当前页面数据可能未保存!';
};
Demo :
在表单页面添加该事件,用户未提交表单时离开会触发提示:
html
<form id="myForm">
<input type="text" name="username" placeholder="请输入用户名">
<button type="submit">提交</button>
</form>
<script>
let isSubmitted = false; // 标记表单是否已提交
document.getElementById("myForm").addEventListener("submit", function() {
isSubmitted = true; // 提交后取消阻止
});
window.onbeforeunload = function() {
if (!isSubmitted) { // 未提交时才触发提示
return '您确定要离开吗?表单数据尚未提交!';
}
};
</script>
6、屏幕录制
通过navigator.mediaDevices.getDisplayMedia()
获取屏幕流,结合MediaRecorder
API录制屏幕内容,录制完成后将视频以webm
格式下载到本地(也可修改代码将视频流上传到后端),适用于在线演示、教程录制等场景。
javascript
// 初始化屏幕录制
const startRecord = async function() {
try {
// 获取屏幕流(需用户授权)
const stream = await navigator.mediaDevices.getDisplayMedia({
video: true, // 录制视频
audio: true // 可选:录制音频(需额外授权)
});
const recordedChunks = []; // 存储录制的视频片段
// 配置录制参数(webm格式,vp9编码,兼容性较好)
const options = { mimeType: "video/webm; codecs=vp9" };
const mediaRecorder = new MediaRecorder(stream, options);
// 监听视频片段数据(每段数据可用时触发)
mediaRecorder.ondataavailable = function(event) {
if (event.data.size > 0) {
recordedChunks.push(event.data); // 保存片段
}
};
// 监听录制结束(用户停止共享屏幕时触发)
mediaRecorder.onstop = function() {
downloadVideo(recordedChunks); // 下载视频
stream.getTracks().forEach(track => track.stop()); // 停止流
};
// 开始录制
mediaRecorder.start();
console.log("录制已开始,点击停止共享结束录制");
} catch (err) {
console.error("录制失败:", err); // 捕获授权失败等错误
}
};
// 下载录制的视频
function downloadVideo(chunks) {
// 合并片段为Blob对象
const blob = new Blob(chunks, { type: "video/webm" });
const url = URL.createObjectURL(blob); // 创建临时URL
const a = document.createElement("a"); // 创建下载链接
a.href = url;
a.download = `screen-record-${new Date().getTime()}.webm`; // 文件名(带时间戳)
document.body.appendChild(a);
a.click(); // 触发下载
document.body.removeChild(a);
URL.revokeObjectURL(url); // 释放临时URL
}
// 调用函数开始录制(需用户主动触发,如点击按钮)
// startRecord();
Demo :
添加开始录制按钮,用户授权后即可录制屏幕,停止共享时自动下载视频:
html
<button onclick="startRecord()">开始屏幕录制</button>
<script>
const startRecord = async function() {
try {
const stream = await navigator.mediaDevices.getDisplayMedia({ video: true, audio: true });
const recordedChunks = [];
const options = { mimeType: "video/webm; codecs=vp9" };
const mediaRecorder = new MediaRecorder(stream, options);
mediaRecorder.ondataavailable = (event) => {
if (event.data.size > 0) recordedChunks.push(event.data);
};
mediaRecorder.onstop = () => {
const blob = new Blob(recordedChunks, { type: "video/webm" });
const url = URL.createObjectURL(blob);
const a = document.createElement("a");
a.href = url;
a.download = `screen-record-${Date.now()}.webm`;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(url);
stream.getTracks().forEach(track => track.stop());
};
mediaRecorder.start();
alert("录制已开始,点击浏览器的「停止共享」结束录制,结束后自动下载视频");
} catch (err) {
alert("录制失败:" + err.message);
}
};
</script>
7、判断横竖屏
通过window.orientation
获取屏幕旋转角度(0/180为竖屏,90/-90为横屏),结合orientationchange
(或resize
)事件监听屏幕方向变化,实时判断当前是横屏还是竖屏,适用于移动端适配场景(如游戏、视频播放)。
javascript
// 判断横竖屏的函数
function hengshuping() {
const orientation = window.orientation;
if (orientation === 180 || orientation === 0) {
alert("竖屏状态!");
// 可选:更新页面提示文本,替代alert
// document.getElementById("orient-tip").textContent = "当前为竖屏";
}
if (orientation === 90 || orientation === -90) {
alert("横屏状态!");
// document.getElementById("orient-tip").textContent = "当前为横屏";
}
}
// 监听屏幕方向变化事件(兼容部分不支持orientationchange的浏览器,用resize兜底)
const eventName = "onorientationchange" in window ? "orientationchange" : "resize";
window.addEventListener(eventName, hengshuping, false);
// 初始加载时执行一次,获取当前方向
hengshuping();
Demo :
在移动端页面显示当前横竖屏状态,避免频繁弹出alert:
html
<div id="orient-tip" style="font-size: 20px; text-align: center; margin-top: 50px;"></div>
<script>
function hengshuping() {
const orientation = window.orientation;
const tipEl = document.getElementById("orient-tip");
if (orientation === 180 || orientation === 0) {
tipEl.textContent = "当前为竖屏状态";
tipEl.style.color = "#0088ff";
} else if (orientation === 90 || orientation === -90) {
tipEl.textContent = "当前为横屏状态";
tipEl.style.color = "#ff6600";
}
}
const eventName = "onorientationchange" in window ? "orientationchange" : "resize";
window.addEventListener(eventName, hengshuping, false);
hengshuping();
</script>
8、横竖屏样式变更
通过CSS的@media all and (orientation: landscape/portrait)
媒体查询,分别定义横屏(landscape)和竖屏(portrait)时的页面样式(如背景色、布局),实现屏幕方向变化时的样式自动切换,无需JavaScript干预。
css
<style>
/* 横屏时的样式:背景色为红色,文本居中 */
@media all and (orientation: landscape) {
body {
background-color: #ff0000;
color: #fff;
text-align: center;
font-size: 24px;
}
}
/* 竖屏时的样式:背景色为绿色,文本左对齐 */
@media all and (orientation: portrait) {
body {
background-color: #00ff00;
color: #333;
text-align: left;
font-size: 18px;
}
}
</style>
<div>旋转屏幕查看样式变化</div>
Demo :
在移动端旋转屏幕,背景色和文本样式会自动切换,无需额外JS:
html
<style>
@media all and (orientation: landscape) {
.container {
background-color: #ff0000;
color: white;
padding: 20px;
border-radius: 8px;
}
}
@media all and (orientation: portrait) {
.container {
background-color: #00ff00;
color: #333;
padding: 15px;
border-radius: 4px;
}
}
</style>
<div class="container">
<h3>横竖屏样式测试</h3>
<p>横屏时背景为红色,竖屏时为绿色</p>
</div>
9、标签页显隐
通过document.hidden
(及兼容属性msHidden
、webkitHidden
)判断当前标签页是否处于隐藏状态,结合visibilitychange
(及兼容事件msvisibilitychange
、webkitvisibilitychange
)监听标签页显隐变化,适用于暂停/恢复视频播放、停止/启动定时器等场景。
javascript
// 兼容不同浏览器的标签页隐显属性和事件名
const { hidden, visibilityChange } = (() => {
let hiddenProp, eventName;
if (typeof document.hidden !== "undefined") {
// 标准属性(Chrome、Firefox、Edge)
hiddenProp = "hidden";
eventName = "visibilitychange";
} else if (typeof document.msHidden !== "undefined") {
// IE浏览器
hiddenProp = "msHidden";
eventName = "msvisibilitychange";
} else if (typeof document.webkitHidden !== "undefined") {
// Safari浏览器
hiddenProp = "webkitHidden";
eventName = "webkitvisibilitychange";
}
return {
hidden: hiddenProp,
visibilityChange: eventName
};
})();
// 标签页显隐变化时的回调函数
const handleVisibilityChange = () => {
const isHidden = document[hidden];
if (isHidden) {
console.log("当前标签页已隐藏(如切换到其他标签)");
// 示例:暂停视频播放
// document.getElementById("myVideo").pause();
} else {
console.log("当前标签页已显示(如切回当前标签)");
// 示例:恢复视频播放
// document.getElementById("myVideo").play();
}
};
// 监听标签页显隐事件
if (hidden && visibilityChange) {
document.addEventListener(visibilityChange, handleVisibilityChange, false);
}
Demo :
显示标签页的实时状态,切换标签时会更新提示:
html
<div id="tab-status" style="font-size: 20px; text-align: center; margin-top: 50px;"></div>
<script>
const { hidden, visibilityChange } = (() => {
let hiddenProp, eventName;
if (typeof document.hidden !== "undefined") {
hiddenProp = "hidden";
eventName = "visibilitychange";
} else if (typeof document.msHidden !== "undefined") {
hiddenProp = "msHidden";
eventName = "msvisibilitychange";
} else if (typeof document.webkitHidden !== "undefined") {
hiddenProp = "webkitHidden";
eventName = "webkitvisibilitychange";
}
return { hidden: hiddenProp, visibilityChange: eventName };
})();
const statusEl = document.getElementById("tab-status");
const updateStatus = () => {
statusEl.textContent = document[hidden] ? "标签页已隐藏" : "标签页已显示";
statusEl.style.color = document[hidden] ? "#ff0000" : "#0088ff";
};
if (hidden && visibilityChange) {
document.addEventListener(visibilityChange, updateStatus, false);
updateStatus(); // 初始状态
}
</script>
10、本地图片预览
通过window.createObjectURL()
(或兼容的window.URL.createObjectURL()
)为本地选择的图片文件创建临时URL,将该URL赋值给img
标签的src
属性,实现无需上传服务器即可预览本地图片,适用于头像上传、图片表单等场景。
html
<!-- 页面结构:文件选择框 + 预览图片 -->
<div class="test">
<input type="file" id="imgInput" accept="image/*"> <!-- accept="image/*"仅允许选择图片 -->
<br>
<img id="previewImg" src="" alt="图片预览" style="margin-top: 20px; max-width: 300px; max-height: 300px;">
</div>
<script>
// 为文件创建临时URL的工具函数(兼容不同浏览器)
const getObjectURL = (file) => {
let url = null;
if (window.createObjectURL !== undefined) {
// 标准方法
url = window.createObjectURL(file);
} else if (window.URL !== undefined) {
// Chrome、Safari
url = window.URL.createObjectURL(file);
} else if (window.webkitURL !== undefined) {
// 旧版Safari
url = window.webkitURL.createObjectURL(file);
}
return url;
};
// 监听文件选择框的变化事件
document.getElementById("imgInput").addEventListener("change", (event) => {
const file = event.target.files[0]; // 获取选择的第一个文件
if (file && file.type.startsWith("image/")) { // 验证是否为图片文件
const previewUrl = getObjectURL(file);
document.getElementById("previewImg").src = previewUrl;
// 可选:释放URL(避免内存泄漏,可在图片上传成功后调用)
// URL.revokeObjectURL(previewUrl);
} else {
alert("请选择有效的图片文件!");
document.getElementById("previewImg").src = ""; // 清空预览
}
});
</script>
Demo :
选择本地图片后,实时在页面上预览,支持常见图片格式(jpg、png、gif等):
html
<div>
<label>选择图片:</label>
<input type="file" id="imgInput" accept="image/jpeg,image/png,image/gif">
<div style="margin-top: 20px;">
<img id="previewImg" src="" alt="预览图" style="max-width: 400px; max-height: 400px; border: 1px solid #eee; padding: 5px;">
</div>
</div>
<script>
const getObjectURL = (file) => {
let url = null;
if (window.createObjectURL || window.URL || window.webkitURL) {
url = (window.createObjectURL || window.URL.createObjectURL || window.webkitURL.createObjectURL).call(window, file);
}
return url;
};
document.getElementById("imgInput").addEventListener("change", (e) => {
const file = e.target.files[0];
if (!file) return;
if (!file.type.match("image/")) {
alert("请选择图片文件(jpg、png、gif)!");
return;
}
const previewUrl = getObjectURL(file);
document.getElementById("previewImg").src = previewUrl;
});
</script>
11、图片预加载
通过创建Image
对象,将需要预加载的图片URL赋值给Image
的src
属性,提前加载图片资源到浏览器缓存中,避免后续使用时出现"白屏"或"加载中"状态,适用于图片轮播、多图展示页面。
javascript
// 存储预加载的Image对象(避免被垃圾回收,确保缓存有效)
const preloadedImages = [];
/**
* 图片预加载函数
* @param {Array<string>} imgUrls - 需要预加载的图片URL数组
* @param {Function} callback - 所有图片加载完成后的回调函数(可选)
*/
function preloader(imgUrls, callback) {
let loadedCount = 0; // 已加载完成的图片数量
const totalCount = imgUrls.length; // 总需要加载的图片数量
if (totalCount === 0) {
callback && callback(); // 无图片需加载,直接执行回调
return;
}
for (let i = 0; i < totalCount; i++) {
const img = new Image(); // 创建Image对象
preloadedImages.push(img); // 存入数组
// 图片加载完成事件
img.onload = function() {
loadedCount++;
console.log(`已预加载图片 ${i+1}/${totalCount}:${imgUrls[i]}`);
// 所有图片加载完成后执行回调
if (loadedCount === totalCount) {
callback && callback();
}
};
// 图片加载失败事件
img.onerror = function() {
console.error(`预加载图片失败:${imgUrls[i]}`);
loadedCount++;
if (loadedCount === totalCount) {
callback && callback();
}
};
// 赋值src,开始加载图片
img.src = imgUrls[i];
}
}
// 示例:预加载2张图片,加载完成后执行回调
const imgList = [
"https://example.com/image1.jpg", // 替换为实际图片URL
"https://example.com/image2.png"
];
preloader(imgList, () => {
console.log("所有图片预加载完成!");
// 后续操作:如显示图片轮播
// document.getElementById("carousel").style.display = "block";
});
Demo :
预加载图片后,点击按钮显示图片,避免加载延迟:
html
<div>
<div id="load-status" style="font-size: 18px; margin-bottom: 20px;">正在预加载图片...</div>
<button id="showBtn" style="display: none;" onclick="showImages()">显示图片</button>
<div id="image-container" style="margin-top: 20px; display: flex; gap: 20px;"></div>
</div>
<script>
const preloadedImages = [];
function preloader(imgUrls, callback) {
let loadedCount = 0;
const totalCount = imgUrls.length;
const statusEl = document.getElementById("load-status");
if (totalCount === 0) {
callback && callback();
return;
}
for (let i = 0; i < totalCount; i++) {
const img = new Image();
preloadedImages.push(img);
img.onload = function() {
loadedCount++;
statusEl.textContent = `预加载进度:${loadedCount}/${totalCount}`;
if (loadedCount === totalCount) {
statusEl.textContent = "所有图片预加载完成!";
document.getElementById("showBtn").style.display = "inline-block"; // 显示按钮
callback && callback();
}
};
img.onerror = function() {
statusEl.textContent = `预加载失败:第${i+1}张图片`;
loadedCount++;
if (loadedCount === totalCount) {
document.getElementById("showBtn").style.display = "inline-block";
callback && callback();
}
};
img.src = imgUrls[i];
}
}
// 预加载示例图片(替换为实际URL)
const imgUrls = [
"https://picsum.photos/400/300?random=1",
"https://picsum.photos/400/300?random=2",
"https://picsum.photos/400/300?random=3"
];
preloader(imgUrls);
// 显示预加载的图片
function showImages() {
const container = document.getElementById("image-container");
preloadedImages.forEach(img => {
img.style.maxWidth = "400px";
container.appendChild(img);
});
document.getElementById("showBtn").disabled = true; // 禁用按钮,避免重复添加
}
</script>
12、字符串脚本化
通过eval()
函数将字符串形式的JavaScript代码解析并执行,可将字符串转换为对象、变量或函数。注意 :eval()
存在XSS漏洞风险(若字符串来源不可信),且影响代码可读性和性能,慎用。
javascript
// 1. 将字符串转换为对象
const objStr = '({ name: "jack", age: 20 })'; // 注意:对象需用()包裹,否则会被解析为代码块
const user = eval(objStr);
console.log("转换后的对象:", user); // 输出:{ name: "jack", age: 20 }
// 2. 将字符串转换为变量引用
const varName = "user";
const userRef = eval(varName); // 等同于直接使用 user 变量
console.log("变量引用:", userRef.name); // 输出:jack
// 3. 执行字符串中的函数代码
const funcStr = 'function add(a, b) { return a + b; }';
eval(funcStr); // 定义 add 函数
const sum = add(3, 5);
console.log("函数执行结果:", sum); // 输出:8
Demo :
演示eval()
的基本用法(仅用于学习,实际开发优先使用JSON.parse()
等安全方法):
html
<div>
<p>eval() 字符串转对象示例:</p>
<button onclick="testEval()">执行eval()</button>
<pre id="result" style="margin-top: 10px; padding: 10px; background: #f5f5f5;"></pre>
</div>
<script>
function testEval() {
const resultEl = document.getElementById("result");
let output = "";
// 安全场景:字符串为固定值(非用户输入)
const safeStr = '({ title: "JavaScript教程", author: "web前端开发" })';
const book = eval(safeStr);
output += "1. 转换后的对象:\n" + JSON.stringify(book, null, 2) + "\n\n";
// 危险场景:若字符串含用户输入,可能注入恶意代码(示例,勿在生产环境使用)
// const userInput = 'alert("XSS攻击!");'; // 恶意输入
// eval(userInput); // 会执行 alert
resultEl.textContent = output;
}
</script>
安全替代方案:
-
若需转换JSON字符串,优先使用
JSON.parse()
(无XSS风险):javascriptconst jsonStr = '{"name": "jack", "age": 20}'; const user = JSON.parse(jsonStr); // 安全可靠
13、递归函数名解耦
通过arguments.callee
引用当前递归函数(arguments
是函数内部的类数组对象,callee
属性指向函数本身),避免递归函数内部硬编码函数名,解决"修改函数名时需同步修改内部引用"的问题。注意 :arguments.callee
在严格模式(use strict
)下禁用,需谨慎使用。
javascript
// 示例1:斐波那契数列(使用arguments.callee解耦函数名)
function fibonacci(n) {
const currentFunc = arguments.callee; // 引用当前函数(fibonacci)
if (n <= 1) return 1; // 基线条件:n=0或1时返回1
return currentFunc(n - 1) + currentFunc(n - 2); // 递归调用,无需硬编码函数名
}
// 测试斐波那契数列
console.log("斐波那契数列第5项:", fibonacci(5)); // 输出:8(1,1,2,3,5,8...)
// 示例2:修改函数名后仍正常工作(无需修改内部代码)
function fib(n) { // 函数名从fibonacci改为fib
const currentFunc = arguments.callee;
if (n <= 1) return 1;
return currentFunc(n - 1) + currentFunc(n - 2); // 内部引用无需修改
}
console.log("斐波那契数列第6项:", fib(6)); // 输出:13
Demo :
对比"硬编码函数名"和"使用arguments.callee"的差异,修改函数名后前者会报错,后者正常运行:
html
<div>
<button onclick="testRecursion()">测试递归解耦</button>
<pre id="recursion-result" style="margin-top: 10px; padding: 10px; background: #f5f5f5;"></pre>
</div>
<script>
function testRecursion() {
const resultEl = document.getElementById("recursion-result");
let output = "";
// 1. 硬编码函数名的递归(修改函数名后会报错)
function factorialHard(n) {
if (n <= 1) return 1;
return n * factorialHard(n - 1); // 硬编码函数名
}
output += "1. 硬编码函数名的阶乘(n=5):" + factorialHard(5) + "\n";
// 修改函数名后,原内部引用失效(报错)
const factHardNew = factorialHard;
try {
factHardNew(6); // 内部仍调用factorialHard,已不存在
} catch (err) {
output += " 修改函数名后报错:" + err.message + "\n\n";
}
// 2. 使用arguments.callee的递归(修改函数名后正常)
function factorialDecoupled(n) {
const currentFunc = arguments.callee;
if (n <= 1) return 1;
return n * currentFunc(n - 1); // 引用当前函数,不依赖函数名
}
output += "2. 解耦的阶乘(n=5):" + factorialDecoupled(5) + "\n";
// 修改函数名后仍正常运行
const factDecoupledNew = factorialDecoupled;
output += " 修改函数名后的阶乘(n=6):" + factDecoupledNew(6) + "\n";
resultEl.textContent = output;
}
</script>
严格模式替代方案 :
若需使用严格模式,可通过"函数表达式"或"命名函数表达式"实现解耦:
javascript
'use strict'; // 严格模式下arguments.callee禁用
// 命名函数表达式(推荐)
const fibonacci = function fib(n) {
if (n <= 1) return 1;
return fib(n - 1) + fib(n - 2); // 内部使用表达式名fib,与外部变量名fibonacci解耦
};
console.log(fibonacci(5)); // 输出:8
14、隐显判断
通过IntersectionObserver
API监听DOM元素与视口(或指定根元素)的交叉状态(是否进入/离开视口),判断元素是否在页面视图内可见,支持配置可见比例阈值(如完全可见时触发),适用于懒加载、滚动动画、曝光统计等场景。
html
<!-- 页面结构:多个高350px的元素,模拟滚动场景 -->
<style>
.container {
height: 1000px; /* 父容器高度,允许滚动 */
padding-top: 20px;
}
.item {
height: 350px;
margin: 20px auto;
border: 2px solid #eee;
border-radius: 8px;
display: flex;
align-items: center;
justify-content: center;
font-size: 24px;
width: 80%;
}
</style>
<div class="container">
<div class="item" data-id="1">元素1(初始不可见)</div>
<div class="item" data-id="2">元素2(初始不可见)</div>
<div class="item" data-id="3">元素3(初始不可见)</div>
</div>
<script>
// 检查浏览器是否支持IntersectionObserver
if (window?.IntersectionObserver) {
// 获取所有需要监听的元素(转换为数组)
const items = [...document.getElementsByClassName("item")];
// 配置IntersectionObserver选项
const observerOptions = {
root: null, // 根元素:null表示视口
rootMargin: "0px 0px", // 根元素的边距(扩展/缩小交叉检测区域)
threshold: 1 // 可见比例阈值:1表示元素完全进入视口时触发回调
};
// 创建IntersectionObserver实例
const observer = new IntersectionObserver((entries) => {
// entries:所有被监听元素的交叉状态数组
entries.forEach((entry) => {
const itemEl = entry.target;
// entry.intersectionRatio:元素可见比例(0=完全不可见,1=完全可见)
if (entry.intersectionRatio === 1) {
itemEl.textContent = `元素${itemEl.dataset.id}(完全可见)`;
itemEl.style.backgroundColor = "#e6f7ff"; // 完全可见时变色
} else {
itemEl.textContent = `元素${itemEl.dataset.id}(部分/完全不可见)`;
itemEl.style.backgroundColor = "#fff"; // 不可见时恢复
}
});
}, observerOptions);
// 开始监听所有元素
items.forEach((item) => observer.observe(item));
// 可选:停止监听(如页面销毁时)
// setTimeout(() => {
// observer.disconnect();
// }, 10000);
} else {
// 兼容不支持的浏览器(如IE),使用滚动事件 fallback
alert("您的浏览器不支持IntersectionObserver,请升级浏览器!");
}
</script>
Demo :
滚动页面,元素完全进入视口时会更新文本和背景色,直观查看可见状态:
html
<style>
body { margin: 0; }
.container {
height: 1200px;
padding-top: 20px;
background-color: #f9f9f9;
}
.item {
height: 350px;
margin: 30px auto;
border: 2px solid #ddd;
border-radius: 8px;
display: flex;
align-items: center;
justify-content: center;
font-size: 24px;
width: 70%;
transition: background-color 0.3s;
}
</style>
<div class="container">
<div class="item" data-id="1">元素1(滚动查看可见性)</div>
<div class="item" data-id="2">元素2(滚动查看可见性)</div>
<div class="item" data-id="3">元素3(滚动查看可见性)</div>
</div>
<script>
if (window?.IntersectionObserver) {
const items = [...document.getElementsByClassName("item")];
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
const el = entry.target;
if (entry.isIntersecting) { // entry.isIntersecting:是否进入视口
const ratio = entry.intersectionRatio.toFixed(2);
el.textContent = `元素${el.dataset.id}(可见比例:${ratio})`;
el.style.backgroundColor = ratio === "1.00" ? "#d4edda" : "#fff3cd";
} else {
el.textContent = `元素${el.dataset.id}(已离开视口)`;
el.style.backgroundColor = "#fff";
}
});
}, { root: null, rootMargin: "0px", threshold: [0, 0.5, 1] }); // 多个阈值:0(进入)、0.5(半可见)、1(完全可见)
items.forEach(item => observer.observe(item));
} else {
alert("浏览器不支持IntersectionObserver,无法查看可见状态!");
}
</script>
15、元素可编辑
通过为DOM元素添加contenteditable="true"
属性,使元素变为可编辑状态(类似textarea
或input
),支持用户直接在页面上修改文本内容,适用于富文本编辑、可修改的标题/描述等场景。
html
<!-- 基础用法:div元素可编辑 -->
<div contenteditable="true" style="padding: 10px; border: 1px solid #ddd; min-height: 50px; max-width: 500px;">
点击此处编辑内容...(支持换行、粘贴文本)
</div>
<!-- 进阶用法:限制仅文本编辑 + 实时显示内容长度 -->
<div style="margin-top: 30px;">
<p>可编辑标题(仅文本,最大长度20):</p>
<h3
contenteditable="true"
style="padding: 5px; border: 1px solid #eee; display: inline-block;"
oninput="updateLength(this)"
onpaste="return false" <!-- 禁止粘贴(可选) -->
>
编辑我
</h3>
<span id="length-tip" style="margin-left: 10px; color: #666;">长度:3/20</span>
</div>
<script>
// 实时更新可编辑元素的内容长度
function updateLength(el) {
const content = el.innerText.trim(); // 获取纯文本内容(排除HTML标签)
const maxLength = 20;
const currentLength = content.length;
// 限制最大长度
if (currentLength > maxLength) {
el.innerText = content.substring(0, maxLength);
// 光标定位到末尾(避免截断后光标错位)
const range = document.createRange();
const sel = window.getSelection();
range.selectNodeContents(el);
range.collapse(false);
sel.removeAllRanges();
sel.addRange(range);
}
// 更新长度提示
const tipEl = document.getElementById("length-tip");
const finalLength = el.innerText.length;
tipEl.textContent = `长度:${finalLength}/${maxLength}`;
tipEl.style.color = finalLength >= maxLength ? "#ff0000" : "#666";
}
</script>
Demo :
可直接编辑页面元素,标题部分限制长度且禁止粘贴,实时显示内容长度:
html
<style>
.editable-card {
width: 600px;
margin: 20px auto;
padding: 20px;
border: 1px solid #eee;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0,0,0,0.05);
}
.editable-title {
font-size: 22px;
margin: 0 0 15px 0;
padding: 5px;
border-bottom: 1px dashed #ddd;
outline: none; /* 取消聚焦时的默认边框 */
}
.editable-content {
min-height: 100px;
padding: 10px;
border: 1px solid #eee;
border-radius: 4px;
outline: none;
white-space: pre-wrap; /* 保留换行符 */
}
.length-tip {
display: block;
margin-top: 10px;
color: #666;
font-size: 14px;
}
</style>
<div class="editable-card">
<h2 class="editable-title" contenteditable="true" oninput="updateTitleLength(this)">
文章标题(可编辑,最大30字)
</h2>
<div class="editable-content" contenteditable="true">
请输入文章内容...
支持换行、段落格式。
可直接粘贴纯文本(粘贴HTML会保留标签,需谨慎)。
</div>
<span id="title-length" class="length-tip">长度:12/30</span>
</div>
<script>
function updateTitleLength(el) {
const content = el.innerText;
const max = 30;
let current = content.length;
if (current > max) {
el.innerText = content.substring(0, max);
// 光标定位到末尾
const range = document.createRange();
const sel = window.getSelection();
range.selectNodeContents(el);
range.collapse(false);
sel.removeAllRanges();
sel.addRange(range);
current = max;
}
document.getElementById("title-length").textContent = `长度:${current}/${max}`;
}
</script>
16、元素属性监听
通过MutationObserver
API监听DOM元素的属性变化(如style
、data-*
、class
等),当属性被修改时触发回调函数,可获取修改前/后的值,适用于动态跟踪元素状态、响应属性变更等场景。
html
<!-- 页面结构:待监听的元素 + 触发属性修改的按钮 -->
<div id="test" style="padding: 20px; border: 1px solid #ddd; display: inline-block;">
测试元素
</div>
<button onclick="handleClick()" style="margin-left: 20px; padding: 8px 16px;">
修改元素属性
</button>
<script>
// 获取待监听的元素
const el = document.getElementById("test");
let attrCount = 1; // 用于生成唯一的data-name属性值
// 配置MutationObserver选项:监听属性变化
const observerOptions = {
attributes: true, // 启用属性监听
attributeOldValue: true, // 记录属性修改前的值(可选,便于对比)
attributeFilter: ["style", "data-name"] // 仅监听指定属性(可选,优化性能)
};
// 创建MutationObserver实例
const observer = new MutationObserver((mutations) => {
// mutations:所有被修改的属性数组
mutations.forEach((mutation) => {
console.log("属性变化详情:");
console.log(" - 被修改的属性:", mutation.attributeName);
console.log(" - 修改前的值:", mutation.oldValue); // 需开启attributeOldValue: true
console.log(" - 修改后的值:", el.getAttribute(mutation.attributeName));
alert(`属性「${mutation.attributeName}」已修改!`);
});
});
// 开始监听元素的属性变化
observer.observe(el, observerOptions);
// 按钮点击事件:修改元素的style和data-name属性
function handleClick() {
// 修改style属性(颜色)
el.setAttribute("style", `padding: 20px; border: 1px solid #ddd; color: ${getRandomColor()};`);
// 修改data-name属性(自定义属性)
el.setAttribute("data-name", `test-${attrCount++}`);
}
// 辅助函数:生成随机颜色
function getRandomColor() {
const letters = "0123456789ABCDEF";
let color = "#";
for (let i = 0; i < 6; i++) {
color += letters[Math.floor(Math.random() * 16)];
}
return color;
}
// 5秒后停止监听(示例,可根据需求调整)
setTimeout(() => {
observer.disconnect();
console.log("5秒后已停止属性监听");
alert("已停止监听元素属性变化!");
}, 5000);
</script>
Demo :
点击按钮修改元素颜色和自定义属性,控制台会打印属性修改前后的值,5秒后停止监听:
html
<style>
#target {
padding: 25px;
border: 2px solid #eee;
border-radius: 8px;
font-size: 18px;
margin: 20px 0;
}
.log-container {
margin-top: 20px;
padding: 15px;
background-color: #f5f5f5;
border-radius: 4px;
max-height: 200px;
overflow-y: auto;
font-family: monospace;
font-size: 14px;
}
</style>
<div id="target">被监听的元素</div>
<button onclick="modifyAttrs()">修改属性(颜色+自定义属性)</button>
<div class="log-container" id="log">
日志:等待属性变化...<br>
</div>
<script>
const target = document.getElementById("target");
const logEl = document.getElementById("log");
let count = 1;
// 添加日志
function addLog(text) {
logEl.innerHTML += `[${new Date().toLocaleTimeString()}] ${text}<br>`;
logEl.scrollTop = logEl.scrollHeight; // 滚动到底部
}
// 创建观察者
const observer = new MutationObserver((mutations) => {
mutations.forEach(mut => {
const attr = mut.attributeName;
const oldVal = mut.oldValue || "无";
const newVal = target.getAttribute(attr);
addLog(`属性「${attr}」变化:旧值=${oldVal} → 新值=${newVal}`);
});
});
// 开始监听
observer.observe(target, {
attributes: true,
attributeOldValue: true,
attributeFilter: ["style", "data-id"]
});
addLog("已开始监听style和data-id属性变化");
// 修改属性的函数
function modifyAttrs() {
const color = `rgb(${Math.random()*255|0}, ${Math.random()*255|0}, ${Math.random()*255|0})`;
target.setAttribute("style", `padding:25px; border:2px solid #eee; border-radius:8px; font-size:18px; color: ${color};`);
target.setAttribute("data-id", `id-${count++}`);
}
// 8秒后停止监听
setTimeout(() => {
observer.disconnect();
addLog("8秒后停止监听属性变化");
}, 8000);
</script>
17、打印DOM元素
通过console.dir()
方法打印DOM元素的详细属性和方法,与console.log()
(仅打印元素标签结构)不同,console.dir()
会以对象形式展示DOM元素的所有属性(如style
、classList
、dataset
等),便于开发调试时查看元素的内部状态。
javascript
// 1. 打印body元素的详细属性
console.log("=== console.log(document.body):仅打印标签结构 ===");
console.log(document.body);
console.log("\n=== console.dir(document.body):打印详细属性 ===");
console.dir(document.body);
// 2. 打印指定元素的属性(如id为"test"的元素)
const testEl = document.getElementById("test");
if (testEl) {
console.log("\n=== console.dir(testEl):指定元素的详细属性 ===");
console.dir(testEl);
// 示例:查看元素的style属性(通过dir可快速定位)
console.log("\n=== testEl.style的详细属性 ===");
console.dir(testEl.style);
}
Demo :
打开浏览器控制台(F12),查看console.log
与console.dir
的差异:
html
<div id="test" class="demo-element" data-type="example" style="color: #ff0000; font-size: 16px;">
调试用元素
</div>
<button onclick="logElement()">点击打印DOM元素到控制台</button>
<script>
function logElement() {
const el = document.getElementById("test");
if (!el) return;
// 1. console.log:打印元素标签结构
console.log("1. console.log(el) → 标签结构:");
console.log(el);
// 2. console.dir:打印元素详细属性(对象形式)
console.log("\n2. console.dir(el) → 详细属性:");
console.dir(el);
// 3. 打印元素的特定属性(如classList、dataset)
console.log("\n3. 元素的classList:");
console.dir(el.classList); // 查看所有类名
console.log("\n4. 元素的dataset(自定义属性):");
console.dir(el.dataset); // 查看data-*属性
}
</script>
调试场景示例 :
当需要查看元素的offsetTop
(距离顶部的距离)、clientWidth
(可视宽度)等布局属性时,console.dir(el)
可快速定位这些属性,无需手动书写el.offsetTop
等代码。
18、激活应用
在移动端开发中,通过<a>
标签的href
属性指定特定协议(如tel
、sms
、wx
),点击链接可唤起设备上的对应应用(如电话、短信、微信),适用于移动端的快捷操作场景(如"一键拨打客服电话""一键发送短信")。
html
<!-- 1. 唤起电话应用:拨打指定号码 -->
<a href="tel:12345678910" style="display: inline-block; margin: 10px; padding: 8px 16px; background: #0088ff; color: #fff; text-decoration: none; border-radius: 4px;">
拨打客服电话:123-4567-8910
</a>
<!-- 2. 唤起短信应用(Android):指定收件人和默认内容 -->
<a href="sms:12345678910,12345678911?body=您好,我想咨询产品问题" style="display: inline-block; margin: 10px; padding: 8px 16px; background: #00cc99; color: #fff; text-decoration: none; border-radius: 4px;">
给客服发短信(Android)
</a>
<!-- 3. 唤起短信应用(iOS):指定收件人和默认内容(协议格式略有差异) -->
<a href="sms:/open?addresses=12345678910,12345678911&body=您好,我想咨询产品问题" style="display: inline-block; margin: 10px; padding: 8px 16px; background: #00cc99; color: #fff; text-decoration: none; border-radius: 4px;">
给客服发短信(iOS)
</a>
<!-- 4. 唤起微信(iOS):通过wx://协议(需微信已安装,兼容性依赖系统版本) -->
<a href="wx://" style="display: inline-block; margin: 10px; padding: 8px 16px; background: #7bb32e; color: #fff; text-decoration: none; border-radius: 4px;">
打开微信(iOS,需已安装)
</a>
<!-- 5. 唤起邮件应用:指定收件人、主题、内容 -->
<a href="mailto:service@example.com?subject=咨询问题&body=我的问题是:" style="display: inline-block; margin: 10px; padding: 8px 16px; background: #ff6600; color: #fff; text-decoration: none; border-radius: 4px;">
发送邮件给客服
</a>
<script>
// 可选:判断设备类型,显示对应平台的短信链接
const isIOS = /iPad|iPhone|iPod/.test(navigator.userAgent) && !window.MSStream;
const smsLinkAndroid = document.querySelector('a[href^="sms:12345678910,12345678911"]');
const smsLinkIOS = document.querySelector('a[href^="sms:/open"]');
if (isIOS) {
smsLinkAndroid.style.display = "none"; // iOS隐藏Android短信链接
} else {
smsLinkIOS.style.display = "none"; // Android隐藏iOS短信链接
}
</script>
Demo :
在移动端打开页面,点击链接会唤起对应应用;在PC端点击会提示"无法打开链接"(需实际移动端测试):
html
<style>
.app-link {
display: inline-block;
margin: 15px;
padding: 10px 20px;
color: #fff;
text-decoration: none;
border-radius: 6px;
font-size: 16px;
transition: opacity 0.3s;
}
.app-link:hover {
opacity: 0.9;
}
.tel-link { background-color: #0088ff; }
.sms-link { background-color: #00cc99; }
.wechat-link { background-color: #7bb32e; }
.mail-link { background-color: #ff6600; }
</style>
<div style="text-align: center; margin-top: 50px;">
<h3>移动端应用唤起测试</h3>
<p>请在手机上打开此页面测试</p>
<a href="tel:4008123456" class="app-link tel-link">
拨打400-812-3456
</a>
<a href="sms:4008123456?body=您好,我想咨询订单问题" class="app-link sms-link" id="smsLink">
给4008123456发短信
</a>
<a href="wx://" class="app-link wechat-link">
打开微信(仅iOS,需已安装)
</a>
<a href="mailto:support@example.com?subject=反馈&body=我的反馈:" class="app-link mail-link">
发送反馈邮件
</a>
</div>
<script>
// 适配iOS和Android的短信链接
const isIOS = /iPad|iPhone|iPod/.test(navigator.userAgent) && !window.MSStream;
const smsLink = document.getElementById("smsLink");
if (isIOS) {
// iOS短信协议格式
smsLink.href = "sms:/open?addresses=4008123456&body=您好,我想咨询订单问题";
} else {
// Android短信协议格式
smsLink.href = "sms:4008123456?body=您好,我想咨询订单问题";
}
</script>
注意事项:
- 不同平台(iOS/Android)的协议格式可能存在差异,需针对性适配;
- 应用唤起依赖设备已安装对应应用,若未安装,链接可能无效;
- 部分浏览器(如Chrome)可能限制非用户主动交互(如点击)的应用唤起,需确保链接通过点击触发。