事件传播、CSS动画、服务器字体与jQuery

第一部分:事件传播机制

1.1 什么是事件传播

老师强调 :事件传播发生在嵌套父子元素 绑定了相同事件类型时,触发其中一个会连带触发其他嵌套元素上的相同事件。就像洋葱层层包裹,碰外皮内层也感知到震动。

触发条件:

  1. 元素间存在嵌套关系(父子、祖孙)
  2. 这些元素绑定了相同事件类型 (如都是click

事件传播方向:

复制代码
捕获阶段(从外到内):
body → container → father → child(触点元素)
                    ↓
冒泡阶段(从内到外):
触点元素 child → father → container → body

1.2 事件传播的两个阶段

阶段 方向 说明
捕获阶段 从祖先到触点 bodycontainerfatherchild
冒泡阶段 从触点到祖先 childfathercontainerbody(默认)

老师强调onclick默认走冒泡阶段 。要观察捕获阶段,需用addEventListener并设置第三个参数为true

代码示例:

复制代码
window.onload = function() {
    var body = document.querySelector("body");
    var container = document.querySelector(".container");
    var father = document.querySelector(".father");
    var child = document.querySelector(".child");
    
    // 方式1:onclick(默认冒泡阶段)
    body.onclick = function() {
        console.log("我是body");
    };
    container.onclick = function(e) {
        e.stopPropagation();   // 阻止事件传播
        console.log("我是container");
    };
    father.onclick = function() {
        console.log("我是father");
    };
    child.onclick = function() {
        console.log("我是child");
    };
    
    // 方式2:addEventListener(可指定阶段)
    // 第三个参数:false=冒泡(默认),true=捕获
    body.addEventListener("click", function() {
        console.log("我是body");
    }, true);  // 捕获阶段
    
    container.addEventListener("click", function(e) {
        e.stopPropagation();   // 在捕获阶段拦截
        console.log("我是container");
    }, true);
    
    father.addEventListener("click", function() {
        console.log("我是father");
    }, true);
    
    child.addEventListener("click", function() {
        console.log("我是child");
    }, true);
};

1.3 阻止事件传播:stopPropagation()

复制代码
// 在任意阶段调用,阻止事件继续传播
container.onclick = function(e) {
    e.stopPropagation();   // 事件到此停止,不再向上/向下传播
    console.log("我是container");
};

执行顺序示例(捕获阶段):

复制代码
点击child时(捕获阶段,第三个参数为true):
执行顺序:body → container → father → child

若在container中调用e.stopPropagation():
执行顺序:body → container(停止,father和child不执行)

1.4 addEventListener 参数说明

复制代码
element.addEventListener(事件类型, 事件函数, 传播阶段);
//                     └─字符串  └─函数   └─true=捕获 / false=冒泡(默认)
参数位置 说明 示例
第1个 事件类型(不带on "click""mouseenter"
第2个 触发时执行的函数 function(){...}
第3个 传播阶段(可选) true(捕获)/ false(冒泡,默认)

第二部分:CSS关键帧动画

2.1 过渡动画 vs 真动画

老师强调transition只是过渡动画,只有开始和结束 两个状态。真动画(关键帧动画)可定义多个中间状态,像动画片由多帧组成。

对比项 过渡动画(transition) 真动画(@keyframes)
状态数量 仅起止两态 可定义多个中间状态
控制方式 自动计算中间值 手动设置关键帧
复杂度 简单 可复杂
适用场景 悬停效果、简单变化 循环动画、复杂特效

2.2 定义关键帧动画:@keyframes

复制代码
/* 第一步:定义动画(关键帧) */
@keyframes change {
    0% {                    /* 起始状态(可省略) */
        width: 200px;
        height: 150px;
        background: yellow;
    }
    30% {
        width: 300px;
        height: 300px;
        background: purple;
    }
    60% {
        width: 400px;
        height: 400px;
        border-radius: 200px;   /* 变成圆形 */
        background: yellowgreen;
    }
    100% {                  /* 结束状态 */
        background: blue;
        width: 500px;
        height: 500px;
    }
}

老师强调from表示初始状态,to表示结束状态,等价于0%100%。各关键帧独立设样式,按百分比进度平滑过渡。

2.3 使用动画:animation 复合属性

复制代码
.demo {
    /* 简写:一次设置所有属性 */
    animation: change 4s ease 2s infinite alternate;
    /*          └名  └时长 └函数 └延迟 └次数 └方向 */
    
    /* 分步写法 */
    animation-name: change;              /* 动画名称 */
    animation-duration: 7s;              /* 持续时间 */
    animation-timing-function: ease;     /* 速度曲线 */
    animation-delay: 2s;                 /* 延迟时间 */
    animation-iteration-count: infinite; /* 执行次数:无限 */
    animation-direction: alternate;      /* 方向:交替执行 */
}

animation 子属性说明:

属性 说明 示例值
animation-name 指定动画名称 change
animation-duration 持续时间 3s1.2s
animation-timing-function 速度曲线 easelinear
animation-delay 延迟时间 0.5s2s
animation-iteration-count 执行次数 1(默认)、infinite
animation-direction 执行方向 alternate(交替)、normal

2.4 动画方向:alternate(交替执行)

老师强调 :默认动画是"结束→跳回开始"再执行,有断层。alternate让动画从结束状态平滑倒放回开始,再正向执行,形成连续往返。

复制代码
/* 普通:结束瞬间跳回开始 */
.demo {
    animation: change 4s ease infinite;
}

/* 交替:结束平滑倒放回开始 */
.demo {
    animation: change 4s ease infinite alternate;
}

2.5 逐字延迟动画示例(I LOVE YOU)

复制代码
<div class="text">
    <span>I</span>
    <span>L</span>
    <span>O</span>
    <span>V</span>
    <span>E</span>
    <span>Y</span>
    <span>O</span>
    <span>U</span>
</div>

.text span {
    animation: change 1.2s ease infinite alternate;
}

/* 每个字符错开延迟,形成依次变化效果 */
.text span:nth-child(2) { animation-delay: .2s; }
.text span:nth-child(3) { animation-delay: .4s; }
.text span:nth-child(4) { animation-delay: .5s; }
.text span:nth-child(5) { animation-delay: .7s; }
.text span:nth-child(6) { animation-delay: .9s; }
.text span:nth-child(7) { animation-delay: 1s; }
.text span:nth-child(8) { animation-delay: 1.2s; }

@keyframes change {
    50% { color: #81ecec; }
    100% {
        color: #55efc4;
        text-shadow: 77px 0px 35px #a29bfe;
        font-size: 200px;
    }
}

老师强调 :动画必须加在span标签上才能分别设延迟,不能加在父容器。第一个不延迟,其余每个递增延迟,形成"推波助澜"的递进效果------像多米诺骨牌,靠时间差形成连贯动画。

2.6 文字描边:-webkit-text-stroke

复制代码
.text span {
    /* 文字描边(WebKit内核) */
    -webkit-text-stroke: 7px #fab1a0;
}

老师强调 :描边相当于给每一个笔画 单独描边框,而非整体勾外轮廓。像字母a中间镂空处,系统没法像人手那样"拐掉掏空",描出来不完美,这是自带处理的局限。

第三部分:服务器字体

3.1 本地字体的问题

老师强调 :用font-family直接指定字体(如宋体)依赖访问者电脑本地安装的字体。别人电脑没安装,就会走默认字体(如微软雅黑),导致显示不一致。

复制代码
/* ❌ 依赖本地字体:别人电脑没宋体就失效 */
div {
    font-family: "宋体";
}

3.2 @font-face 定义服务器字体

老师强调 :把字体当作资源随服务器部署,访问时加载该资源,不依赖用户本地字体 。分两步:先用@font-face定义,再用font-family引用。

复制代码
/* 第一步:定义服务器字体 */
@font-face {
    font-family: "demo";   /* 自定义字体名(随意) */
    src: url("font/HuXiaoBoSaoBaoTi-2.otf");  /* 字体文件路径 */
    /* format: .otf → "opentype"  .ttf → "truetype" */
}

/* 第二步:使用字体 */
div {
    font-family: "demo";   /* 引用上面定义的名字 */
    font-size: 80px;
    color: aqua;
}

多字体示例:

复制代码
@font-face {
    font-family: "demo";
    src: url("font/HuXiaoBoSaoBaoTi-2.otf");
}

@font-face {
    font-family: "text";
    src: url("font/TravelightpersonaluseBold-z8o5D.ttf");
}

@font-face {
    font-family: "run";
    src: url("font/QianTuXiaoTuTi-2.ttf");
}

div { font-family: "demo"; font-size: 80px; color: aqua; }
p { font-family: "text"; font-size: 80px; color: gold; }
span { font-family: "run"; font-size: 80px; color: plum; }

3.3 字体文件格式

后缀 format值 说明
.otf opentype OpenType字体
.ttf truetype TrueType字体

老师强调 :字体文件只能是.otf.ttfformat写错会导致浏览器识别不了,要么不写让浏览器自己识别,要么写对。

3.4 字体兼容性注意

老师强调 :自定义字体可能不支持中文,写中文会没效果,需实测确认。有的字体只认中文、有的只认英文、有的都认。下载后需自己打开测试。

复制代码
<!-- 测试效果 -->
<div>你好</div>      <!-- 可能无效 -->
<div>hello</div>     <!-- 可能有效 -->
<p>hello你好</p>     <!-- 混合测试 -->

第四部分:jQuery 核心语法

4.1 什么是jQuery

老师强调 :jQuery是一个JavaScript库,对原生JS进行封装,简化了DOM操作、事件处理和Ajax请求。它不改变原生语法规则,只是把常用功能包成方法供调用。

原生JS vs jQuery:

对比项 原生JS jQuery
查找元素 document.querySelectorAll() $()
遍历绑定 手动for循环 内置循环,自动处理
隐藏元素 element.style.display = "none" .hide()
获取元素 DOM元素 jQuery对象(JDom)

4.2 引入jQuery

复制代码
<!-- 方式1:在线引入 -->
<script src="https://cdn.jsdelivr.net/npm/jquery@3.6.0/dist/jquery.min.js"></script>

<!-- 方式2:本地引入 -->
<script src="js/jquery.js"></script>

老师强调 :jQuery本质就是个.js文件,引入方式和普通JS完全一样。官网展示的是源码,复制即下载。

4.3 文档就绪函数

复制代码
// 方式1:完整写法
$(document).ready(function() {
    // DOM加载完成后执行
});

// 方式2:简写(推荐)
$(function() {
    // DOM加载完成后执行
});

老师强调$(document).ready()等价于原生JS的window.onload,都是等HTML加载完再执行,避免找不到元素。简写为$(function(){...})

4.4 jQuery核心语法:$()

复制代码
// $() 是jQuery的语法标识,用于选择元素
$(selector).action()
//  └─选择器  └─操作

// 示例
$(".btn").click(function() { ... });   // 选择class为btn的元素绑定点击
$("#id1").hide();                      // 选择id为id1的元素隐藏
$("p").css("color", "red");            // 选择所有p标签改颜色

4.5 jQuery对象 vs DOM对象

老师强调$()获取的是jQuery对象(JDom) ,原生document.querySelector()获取的是DOM元素 。两者的方法不互通:JDom只能调jQuery方法,DOM只能调原生方法。

获取方式 返回类型 可调方法
$(".btn") jQuery对象(JDom) .hide().css().click()
document.querySelector(".btn") DOM元素 style.displayaddEventListener()

相互转换:

复制代码
// JDom → DOM(用下标)
var jdom = $(".btn");
var dom = jdom[0];          // 或 jdom.get(0)
dom.style.background = "red";  // 调原生方法

// DOM → JDom(用$()包裹)
var dom = document.querySelector(".btn");
var jdom = $(dom);
jdom.css("background", "red");  // 调jQuery方法

老师强调 :中括号取值会脱离jQuery封装 ,变成原生DOM,之后只能用原生方法。反过来用$()包裹原生DOM就能用jQuery方法。

复制代码
// ❌ 常见错误:混用方法
$(".test")[0].css("color", "red");   // 报错![0]后是DOM,无css方法

// ✅ 正确用法
$(".test").css("color", "red");      // JDom调jQuery方法
$(".test")[0].style.color = "red";   // DOM调原生方法

第五部分:jQuery DOM操作

5.1 内容操作

方法 说明 对应原生
.text() 获取/设置纯文本 innerText
.html() 获取/设置HTML内容 innerHTML
.val() 获取/设置表单值 value
.attr() 获取/设置属性 getAttribute/setAttribute

规则: 括号内传参=设置不传参=获取

复制代码
// 获取
var text = $(".demo").text();
var html = $(".demo").html();
var value = $(".info").val();
var attr = $(".demo").attr("index");

// 设置
$(".demo").text("新文本");
$(".demo").html("<h1>新标题</h1>");
$(".info").val("新值");
$(".demo").attr("index", "5");

5.2 样式操作:.css()

复制代码
// 获取样式(传一个参数)
var bg = $(".demo").css("background-color");

// 设置样式(传两个参数)
$(".demo").css("background", "yellow");
$(".demo").css("color", "#888");

// 批量设置(传对象)
$(".demo").css({
    "color": "#888",
    "text-decoration": "line-through"
});

5.3 类操作

方法 说明
.addClass("类名") 添加类
.removeClass("类名") 移除类
.toggleClass("类名") 切换类
.hasClass("类名") 判断是否有某类

5.4 添加元素

方法 说明 位置
.append(内容) 在内部末尾添加 子元素最后
.prepend(内容) 在内部开头添加 子元素最前
.after(内容) 在元素后面添加 兄弟元素
.before(内容) 在元素前面添加 兄弟元素
复制代码
// 示例
$(".demo").append("<p>追加内容</p>");   // 内部末尾
$(".demo").prepend("<p>开头内容</p>");  // 内部开头
$("p").after("<p>后面的兄弟</p>");      // 后面
$("p").before("<p>前面的兄弟</p>");     // 前面

// jQuery方式添加表格行
$("tbody").append(
    '<tr>' +
    '<td index="' + idCard + '">' + $(".info").val() + '</td>' +
    '<td>' +
    '<input type="button" class="finish" value="完成">' +
    '<input type="button" class="update" value="修改">' +
    '<input type="button" class="delete" value="删除">' +
    '</td>' +
    '</tr>'
);

老师强调 :jQuery添加节点不用createElement,直接传HTML字符串即可,比原生简洁很多。注意引号嵌套------外单内双或外双内单。

5.5 删除元素

方法 说明
.remove() 删除元素本身(连壳带肉)
.empty() 清空内部内容(留壳)
复制代码
$(".demo").remove();   // 删除整个元素
$(".demo").empty();    // 清空内部,元素保留

5.6 关系查找

方法 说明
.parent() 直接父元素
.parents() 所有祖先元素
.prev() 前一个兄弟元素
.next() 后一个兄弟元素
.children() 所有子元素
.find(选择器) 后代中查找
复制代码
// 示例
$(this).parent().remove();              // 删除父元素
$(this).parent().prev().css("color", "red");  // 父级的前一个兄弟
$(this).parents("ul").css("border", "1px solid red");  // 祖先ul

5.7 eq() 方法:按索引取元素

老师强调 :jQuery对象不能用[0]下标(会转成DOM),要用.eq(索引)取指定位置的JDom元素。

复制代码
// ❌ 错误:用下标会变成DOM,失去jQuery方法
$("td")[0].css("color", "red");   // 报错!

// ✅ 正确:用eq()保持JDom
$("td").eq(0).css("color", "red");   // 第一个td变红

第六部分:jQuery事件绑定

6.1 事件绑定方式

复制代码
// 方式1:事件函数绑定(去掉on前缀)
$(".btn").click(function() {
    alert("点击了");
});

// 常用事件:click, mouseenter, mouseleave, focus, blur
// 注意:原生onclick → jQuery click(去on)

6.2 hover 特殊事件

老师强调hover是jQuery自行封装的,原生JS没有。它把鼠标进入和离开合并为一个方法。

复制代码
// hover:鼠标进入触发第一个函数,离开触发第二个
$(".menu > li").hover(
    function() {
        // 鼠标放上去:展开
        $(this).find(".model").css("height", "200px");
    },
    function() {
        // 鼠标离开:收起
        $(this).find(".model").css("height", "0");
    }
);

6.3 on() 事件绑定

复制代码
// 方式1:普通绑定(事件类型 + 函数)
$(".btn").on("click", function() {
    alert("点击了");
});

// 方式2:委托绑定(事件类型 + 目标元素 + 函数)
// ⭐ 用于绑定动态生成的未来元素
$("tbody").on("click", ".delete", function() {
    $(this).parent().parent().remove();
});

老师强调 :委托绑定中,代理容器必须是原本就存在的祖先元素 (如tbodytablebody),不能是后来动态生成的。on会循环批量给所有匹配元素绑事件,不论未来新增几个都能生效。

普通绑定 vs 委托绑定:

对比项 普通绑定 委托绑定
参数 (事件, 函数) (事件, 目标元素, 函数)
适用元素 已存在的元素 动态生成的未来元素
容器要求 目标元素本身 必须是静态祖先

6.4 动态事件绑定

复制代码
// 方式1:直接事件函数(动态元素不适用)
$(".delete").click(function() {
    // 只对初始存在的.delete生效
});

// 方式2:on委托(动态元素适用)⭐推荐
$("tbody").on("click", ".delete", function() {
    $(this).parent().parent().remove();
});

// 方式3:每次添加后重新绑定(繁琐,不推荐)
function bindEvents() {
    $(".delete").click(function() { ... });
}

第七部分:jQuery To-Do List 完整实现

复制代码
$(function() {
    var idCard = 0;          // 唯一标识计数器
    var targetIdCard;        // 要修改的目标行标识

    // 1. 添加 / 修改(共用同一按钮)
    $(".btn").click(function() {
        var text = $(".info").val().trim();
        if (text) {
            if ($(".btn").val() == "添加") {
                // 添加
                $("tbody").append(
                    '<tr>' +
                    '<td index="' + idCard + '">' + text + '</td>' +
                    '<td>' +
                    '<input type="button" class="finish" value="完成">' +
                    '<input type="button" class="update" value="修改">' +
                    '<input type="button" class="delete" value="删除">' +
                    '</td>' +
                    '</tr>'
                );
                idCard++;
                $(".info").val("");
            } else {
                // 修改
                var list = $("table tr td:nth-child(1)");
                for (var i = 0; i < list.length; i++) {
                    if (list.eq(i).attr("index") == targetIdCard) {
                        list.eq(i).text(text);
                        $(".info").val("");
                        $(".btn").val("添加");
                        $(".btn").css("background", "#feca57");
                    }
                }
            }
        }
    });

    // 2. 删除(委托绑定未来元素)
    $("tbody").on("click", ".delete", function() {
        $(this).parent().parent().remove();
    });

    // 3. 完成(委托绑定未来元素)
    $("tbody").on("click", ".finish", function() {
        $(this).parent().prev().css("color", "#888");
        $(this).parent().prev().css("text-decoration", "line-through");
        $(this).css("background", "#aaa");
    });

    // 4. 修改回显(委托绑定未来元素)
    $("tbody").on("click", ".update", function() {
        var td = $(this).parent().prev();
        $(".info").val(td.text());
        $(".btn").val("修改");
        $(".btn").css("background", "#48dbfb");
        targetIdCard = td.attr("index");
    });
});

附录一:事件传播代码示例(事件传播.html)

复制代码
<!DOCTYPE html>
<html>
<head>
<style>
    .container { width: 600px; height: 600px; background: gold; }
    .father { width: 400px; height: 400px; background: pink; }
    .child { width: 200px; height: 200px; background: plum; }
</style>
<script>
window.onload = function() {
    var body = document.querySelector("body");
    var container = document.querySelector(".container");
    var father = document.querySelector(".father");
    var child = document.querySelector(".child");
    
    // 捕获阶段演示
    body.addEventListener("click", function() {
        console.log("我是body");
    }, true);
    container.addEventListener("click", function(e) {
        e.stopPropagation();   // 在此拦截
        console.log("我是container");
    }, true);
    father.addEventListener("click", function() {
        console.log("我是father");
    }, true);
    child.addEventListener("click", function() {
        console.log("我是child");
    }, true);
}
</script>
</head>
<body>
    <div class="container">
        <div class="father">
            <div class="child"></div>
        </div>
    </div>
</body>
</html>

附录二:jQuery方法速查表

选择器

写法 说明
$("p") 所有p标签
$("#id1") id为id1的元素
$(".demo") class为demo的元素

内容操作

方法 说明
.text() 获取/设置纯文本
.html() 获取/设置HTML
.val() 获取/设置表单值
.attr() 获取/设置属性

样式操作

方法 说明
.css() 获取/设置CSS样式
.addClass() 添加类
.removeClass() 移除类
.toggleClass() 切换类

DOM操作

方法 说明
.append() 内部末尾添加
.prepend() 内部开头添加
.after() 后面添加
.before() 前面添加
.remove() 删除自身
.empty() 清空内部

关系查找

方法 说明
.parent() 直接父级
.parents() 所有祖先
.prev() 前一个兄弟
.next() 后一个兄弟
.eq(索引) 按索引取元素

事件绑定

方法 说明
.click(fn) 点击事件
.hover(fn1, fn2) 悬停事件
.on(event, fn) 普通绑定
.on(event, selector, fn) 委托绑定
相关推荐
XLYcmy35 分钟前
HTML/CSS/JS 基础与 Vue 技术栈深度解析
java·前端·css·html·vue3·vue2·api
风月说与山鬼1 小时前
四、Tailwind CSS——间距与尺寸
前端·css·tailwind css
Alphapeople1 小时前
moveit真机部署
linux·运维·服务器
风月说与山鬼1 小时前
三、Tailwind CSS——排版与文字样式
前端·css·tailwind css
打团从来人不齐2 小时前
Linux服务器搭建笔记-008:台式机共享WIFI
linux·服务器·笔记
Titan20243 小时前
HTTPS基础知识梳理
服务器·网络·c++·网络协议·学习·http·https
智脑API3 小时前
CCSwitch Claude Code 如何配置 MCP?服务器启动、工具权限与安全测试
运维·服务器·claude·codex·ccswitch
马优晨3 小时前
pom.xml 中 jquery webjars 依赖解释
xml·前端·jquery·jquery webjars·webjars依赖
里欧跑得慢4 小时前
AI 驱动的 UI 国际化方案自动适配:从单一语言到多语言布局的智能转换
前端·css·flutter·web