day5_js

Day5

类和样式
  1. 这里的属性:elem.classList。(可以获取当前元素的所有的class名称)

elem.classList 是一个特殊的对象,它具有 add/remove/toggle 单个类的方法。

classList 的方法:

  • elem.classList.add/remove(class) —— 添加/移除类。
  • elem.classList.toggle(class) —— 如果类不存在就添加类,存在就移除它。
  • elem.classList.contains(class) —— 检查给定类,返回 true/false
  1. 样式

    对于多词(multi-word)属性,使用驼峰式 camelCase:

    background-color  => elem.style.backgroundColor
    z-index           => elem.style.zIndex
    border-left-width => elem.style.borderLeftWidth
    

    删除一个样式:方法 elem.style.removeProperty('style property')。所以,我们可以像这样删除一个属性/ 或者获取元素style并将其设置为空字符串即可

    document.body.style.background = 'red'; //将 background 设置为红色
    setTimeout(() => document.body.style.removeProperty('background'), 1000);
    

    当然也可以使用elem.style.cssText去设置多个css属性

  2. 计算样式

    **style 属性仅对 "style" 特性(attribute)值起作用,而没有任何 CSS 级联(cascade)。**请注意是特性而不是属性。解决办法:(计算样式)

    getComputedStyle``(`element`,` `[`pseudo`]``) // element为读取样式值的元素
    
    <head>
      <style> body { color: red; margin: 5px } </style>
    </head>
    <body>
    
      The red text
      <script>
        alert(document.body.style.color); // 空的
        alert(document.body.style.marginTop); // 空的
      </script>
    </body>
    

    如上面代码段所示,是拿不到style内容的,因为是级联起来的

    <head>
      <style> body { color: red; margin: 5px } </style>
    </head>
    <body>
      <script>
        let computedStyle = getComputedStyle(document.body);
    
        // 现在我们可以读取它的 margin 和 color 了
    
        alert( computedStyle.marginTop ); // 5px
        alert( computedStyle.color ); // rgb(255, 0, 0)
      </script>
    </body>
    
元素大小与滚动
  1. offsetParent 是最接近的祖先(ancestor),在浏览器渲染期间,它被用于计算坐标。

    1. 最近的祖先为下列之一:
      1. CSS 定位的(positionabsoluterelativefixedsticky),
      2. <td><th><table>
      3. <body>
    <main style="position: relative" id="main">
      <article>
        <div id="example" style="position: absolute; left: 180px; top: 180px">...</div>
      </article>
    </main>
    <script>
      //这里通过id进行获取元素
      alert(example.offsetParent.id); // main
      alert(example.offsetLeft); // 180(注意:这是一个数字,不是字符串 "180px")
      alert(example.offsetTop); // 180
    </script>
    

    有以下几种情况下,offsetParent 的值为 null

    1. 对于未显示的元素(display:none 或者不在文档中)。
    2. 对于 <body><html>
    3. 对于带有 position:fixed 的元素。
  2. offsetWidth以及offsetHeight为元素的整体大小(包括border,padding,width|height)


  3. clientTop\clientLeft大多数情况是表示:上边框宽度以及左边框宽度

  4. clientWidth\clientHeight大多数情况是表示:内容区域宽高(包括padding,但是不包括滚动条)

    总结

    元素具有以下几何属性:

    • offsetParent —— 是最接近的 CSS 定位的祖先,或者是 tdthtablebody
    • offsetLeft/offsetTop —— 是相对于 offsetParent 的左上角边缘的坐标。
    • offsetWidth/offsetHeight —— 元素的“外部” width/height,边框(border)尺寸计算在内。
    • clientLeft/clientTop —— 从元素左上角外角到左上角内角的距离。对于从左到右显示内容的操作系统来说,它们始终是左侧/顶部 border 的宽度。而对于从右到左显示内容的操作系统来说,垂直滚动条在左边,所以 clientLeft 也包括滚动条的宽度。
    • clientWidth/clientHeight —— 内容的 width/height,包括 padding,但不包括滚动条(scrollbar)。
    • scrollWidth/scrollHeight —— 内容的 width/height,就像 clientWidth/clientHeight 一样,但还包括元素的滚动出的不可见的部分。
    • scrollLeft/scrollTop —— 从元素的左上角开始,滚动出元素的上半部分的 width/height。

    除了 scrollLeft/scrollTop 外,所有属性都是只读的。如果我们修改 scrollLeft/scrollTop,浏览器会滚动对应的元素。

Windows大小和滚动
  • 文档可见部分的 width/height(内容区域的 width/height):document.documentElement.clientWidth/clientHeight

  • 整个文档的 width/height,其中包括滚动出去的部分:

    let scrollHeight = Math.max(
      document.body.scrollHeight, document.documentElement.scrollHeight,
      document.body.offsetHeight, document.documentElement.offsetHeight,
      document.body.clientHeight, document.documentElement.clientHeight
    );
    

滚动:

  • 读取当前的滚动:window.pageYOffset/pageXOffset
  • 更改当前的滚动:
    • window.scrollTo(pageX,pageY) —— 绝对坐标,
    • window.scrollBy(x,y) —— 相对当前位置进行滚动,
    • elem.scrollIntoView(top) —— 滚动以使 elem 可见(elem 与窗口的顶部/底部对齐)。

页面上的任何点都有坐标:

  1. 相对于窗口的坐标 —— elem.getBoundingClientRect()

  2. 相对于文档的坐标 —— elem.getBoundingClientRect() 加上当前页面滚动。

    // 获取元素的文档坐标
    function getCoords(elem) {
      let box = elem.getBoundingClientRect();
    
      return {
        top: box.top + window.pageYOffset,
        right: box.right + window.pageXOffset,
        bottom: box.bottom + window.pageYOffset,
        left: box.left + window.pageXOffset
      };
    }
    

窗口坐标非常适合和 position:fixed 一起使用,文档坐标非常适合和 position:absolute 一起使用。

这两个坐标系统各有利弊。有时我们需要其中一个或另一个,就像 CSS positionabsolutefixed 一样。

事件

我们不能为一个事件分配多个处理程序。如何解决?

使用addEventListener—Web 标准的开发者很早就了解到了这一点,并提出了一种使用特殊方法 addEventListenerremoveEventListener 来管理处理程序的替代方法。它们没有这样的问题。

// 语法
element.addEventListener(event, handler[, options]);【event为事件名,handle为处理程序】
element.removeEventListener(event, handler[, options]);

但是请注意,在移除相同的函数的过程中,所传递的函数一定是要有定义函数名称的函数,例子:
function handler() {
  alert( 'Thanks!' );
}

input.addEventListener("click", handler);
// ....
input.removeEventListener("click", handler);

这里有 3 种分配事件处理程序的方式:

  1. HTML 特性(attribute):onclick="..."
  2. DOM 属性(property):elem.onclick = function
  3. 方法(method):elem.addEventListener(event, handler[, phase]) 用于添加,removeEventListener 用于移除。

HTML 特性很少使用,因为 HTML 标签中的 JavaScript 看起来有些奇怪且陌生。而且也不能在里面写太多代码。

DOM 属性用起来还可以,但我们无法为特定事件分配多个处理程序。在许多场景中,这种限制并不严重。

最后一种方式是最灵活的,但也是写起来最长的。有少数事件只能使用这种方式。例如 transtionendDOMContentLoaded(上文中讲到了)。addEventListener 也支持对象和类作为事件处理程序。在这种情况下,如果发生事件,则会调用 handleEvent 方法。

<button id="elem">Click me</button>

<script>
  class Menu {
    handleEvent(event) {
      switch(event.type) {
        case 'mousedown':
          elem.innerHTML = "Mouse button pressed";
          break;
        case 'mouseup':
          elem.innerHTML += "...and released.";
          break;
      }
    }
  }

  let menu = new Menu();
  elem.addEventListener('mousedown', menu);
  elem.addEventListener('mouseup', menu);
</script>

无论你如何分类处理程序 —— 它都会将获得一个事件对象作为第一个参数。该对象包含有关所发生事件的详细信息。

冒泡与捕获

当一个事件发生时 —— 发生该事件的嵌套最深的元素被标记为“目标元素”(event.target)。

  • 然后,事件从文档根节点向下移动到 event.target,并在途中调用分配了 addEventListener(..., true) 的处理程序(true{capture: true} 的一个简写形式)[capture设置为true意味着捕获阶段会进行事件监听]。
  • 然后,在目标元素自身上调用处理程序。
  • 然后,事件从 event.target 冒泡到根,调用使用 on<event>、HTML 特性(attribute)和没有第三个参数的,或者第三个参数为 false/{capture:false}addEventListener 分配的处理程序。

每个处理程序都可以访问 event 对象的属性:

  • event.target —— 引发事件的层级最深的元素。
  • event.currentTarget(=this)—— 处理事件的当前元素(具有处理程序的元素)
  • event.eventPhase —— 当前阶段(capturing=1,target=2,bubbling=3)。

任何事件处理程序都可以通过调用 event.stopPropagation() 来停止事件,但不建议这样做,因为我们不确定是否确实不需要冒泡上来的事件,也许是用于完全不同的事情。

捕获阶段很少使用,通常我们会在冒泡时处理事件。这背后有一个逻辑。

在现实世界中,当事故发生时,当地警方会首先做出反应。他们最了解发生这件事的地方。然后,如果需要,上级主管部门再进行处理。

事件处理程序也是如此。在特定元素上设置处理程序的代码,了解有关该元素最详尽的信息。特定于 <td> 的处理程序可能恰好适合于该 <td>,这个处理程序知道关于该元素的所有信息。所以该处理程序应该首先获得机会。然后,它的直接父元素也了解相关上下文,但了解的内容会少一些,以此类推,直到处理一般性概念并运行最后一个处理程序的最顶部的元素为止。

事件委托

它通常用于为许多相似的元素添加相同的处理,但不仅限于此。

算法:

  1. 在容器(container)上放一个处理程序。
  2. 在处理程序中 —— 检查源元素 event.target
  3. 如果事件发生在我们感兴趣的元素内,那么处理该事件。

但是也有问题:

  • 首先,事件必须冒泡。而有些事件不会冒泡。此外,低级别的处理程序不应该使用 event.stopPropagation()
  • 其次,委托可能会增加 CPU 负载,因为容器级别的处理程序会对容器中任意位置的事件做出反应,而不管我们是否对该事件感兴趣。但是,通常负载可以忽略不计,所以我们不考虑它
浏览器默认行为

有很多默认的浏览器行为:

  • mousedown —— 开始选择(移动鼠标进行选择)。
  • <input type="checkbox"> 上的 click —— 选中/取消选中的 input
  • submit —— 点击 <input type="submit"> 或者在表单字段中按下 Enter 键会触发该事件,之后浏览器将提交表单。
  • keydown —— 按下一个按键会导致将字符添加到字段,或者触发其他行为。
  • contextmenu —— 事件发生在鼠标右键单击时,触发的行为是显示浏览器上下文菜单。
  • ……还有更多……

如果我们只想通过 JavaScript 来处理事件,那么所有默认行为都是可以被阻止的。

想要阻止默认行为 —— 可以使用 event.preventDefault()return false。第二个方法只适用于通过 on<event> 分配的处理程序。

addEventListenerpassive: true 选项告诉浏览器该行为不会被阻止。这对于某些移动端的事件(像 touchstarttouchmove)很有用,用以告诉浏览器在滚动之前不应等待所有处理程序完成。

如果默认行为被阻止,event.defaultPrevented 的值会变成 true,否则为 false。【这是个属性】–可以完成防止事件冒泡的需求。

<p>Right-click for the document menu (added a check for event.defaultPrevented)</p>
<button id="elem">Right-click for the button menu</button>

<script>
  elem.oncontextmenu = function(event) {
    event.preventDefault();
    alert("Button context menu");
  };

  document.oncontextmenu = function(event) {
    if (event.defaultPrevented) return;

    event.preventDefault();
    alert("Document context menu");
  };
</script>
鼠标事件

在事件的处理过程中是有先后顺序的:会遵循 mousedownmouseupclick 的顺序调用处理程序。

鼠标按钮

在click事件中指定了鼠标按钮为左键,contextmenu指定鼠标按钮为右键。但是对于mousedown和mouseup事件它不会判断是鼠标的左键还是右键,因此我们需要有的时候来进行判断,判断属性为:event.button

在这里插入图片描述

组合键(鼠标事件也可能需要键盘配合才可以触发)

事件属性:

  • shiftKey:Shift
  • altKey:Alt(或对于 Mac 是 Opt)
  • ctrlKey:Ctrl
  • metaKey:对于 Mac 是 Cmd
<button id="button">Alt+Shift+Click on me!</button>
// 需要同时按住alt和shift并点击才可以触发
<script>
  button.onclick = function(event) {
    if (event.altKey && event.shiftKey) {
      alert('Hooray!');
    }
  };
</script>

坐标

所有的鼠标事件都提供了两种形式的坐标:

  1. 相对于窗口的坐标:clientXclientY
  2. 相对于文档的坐标:pageXpageY

防止复制(防止默认事件发生)

  1. mousedown 的默认浏览器操作是文本选择,如果它对界面不利,则应避免它。

  2. 如果我们想禁用选择以保护我们页面的内容不被复制粘贴,那么我们可以使用另一个事件:oncopy

<div oncopy="alert('Copying forbidden!');return false">
  Dear user,
  The copying is forbidden for you.
  If you know JS or HTML, then you can get everything from the page source though.
</div>

如果你试图在 <div> 中复制一段文本,这是行不通的,因为默认行为 oncopy 被阻止了。

鼠标移动

mouseover和mouseout这两个事件并不是很好用,因为mouseover具有冒泡特性,因此假如鼠标从父元素进入子元素时,父元素会出现两个处理程序(一个mouseout【因为移动到子元素了】,还有一个mouseover【冒泡产生】)

解决:1.使用relatedTarget进行判断 2. 使用 mouseenter\mouseleave 两个事件

对于mouseenter\mouseleave

与前两个事件差不多,鼠标移入和移出时触发,但是有两个重要的区别:

  1. 元素内部与后代之间的转换不会产生影响。
  2. 事件 mouseenter/mouseleave 不会冒泡。

总结:

我们讲了 mouseovermouseoutmousemovemouseentermouseleave 事件。

以下这些内容要注意:

  • 快速移动鼠标可能会跳过中间元素。
  • mouseover/outmouseenter/leave 事件还有一个附加属性:relatedTarget。这就是我们来自/到的元素,是对 target 的补充。

即使我们从父元素转到子元素时,也会触发 mouseover/out 事件。浏览器假定鼠标一次只会位于一个元素上 —— 最深的那个。

mouseenter/leave 事件在这方面不同:它们仅在鼠标进入和离开元素时才触发。并且它们不会冒泡。

拖拽

关键部分:(ball就是一个元素)

  1. 事件流:ball.mousedowndocument.mousemoveball.mouseup(不要忘记取消原生 ondragstart)。
  2. 在拖动开始时 —— 记住鼠标指针相对于元素的初始偏移(shift):shiftX/shiftY,并在拖动过程中保持它不变。
  3. 使用 document.elementFromPoint 检测鼠标指针下的 “droppable” 的元素。
 // 实现一个拖拽

键盘事件

按一个按键总是会产生一个键盘事件,无论是符号键,还是例如 Shift 或 Ctrl 等特殊按键。唯一的例外是有时会出现在笔记本电脑的键盘上的 Fn 键。它没有键盘事件,因为它通常是被在比 OS 更低的级别上实现的。

键盘事件:

  • keydown —— 在按下键时(如果长按按键,则将自动重复),【event对象的repeat属性设置为true了】
  • keyup —— 释放按键时。

键盘事件的主要属性:

  • code —— “按键代码”("KeyA""ArrowLeft" 等),特定于键盘上按键的物理位置。
  • key —— 字符("A""a" 等),对于非字符(non-character)的按键,通常具有与 code 相同的值。
  • 也就是说event.key是区分大小写的,但是event.code不区分大小写返回的是物理上的位置,比如‘KeyZ’

过去,键盘事件有时会被用于跟踪表单字段中的用户输入。这并不可靠,因为输入可能来自各种来源。我们有 inputchange 事件来处理任何输入(稍后我们会在 事件:change,input,cut,copy,paste 一章中进行介绍)。它们在任何类型的输入(包括复制粘贴或语音识别)后触发。

滚动
  1. 滑到页面底部则添加内容至网页
function populate() {
  while(true) {
    // 文档末端
    let windowRelativeBottom = document.documentElement.getBoundingClientRect().bottom;

    // 如果用户将页面滚动的距离不够远(文档末端距窗口底部 >100px)
    if (windowRelativeBottom > document.documentElement.clientHeight + 100) break;

    // 让我们添加更多数据
    document.body.insertAdjacentHTML("beforeend", `<p>Date: ${new Date()}</p>`);
  }
}
表单

表单导航:

  • document.forms

    一个表单元素可以通过 document.forms[name/index] 访问到。【请注意是表单控件里面的name属性】

<form name="my">
  <input name="one" value="1">
  <input name="two" value="2">
</form>

<script>
  // 获取表单
  let form = document.forms.my; // <form name="my"> 元素

  // 获取表单中的元素
  let elem = form.elements.one; // <input name="one"> 元素

  alert(elem.value); // 1
</script>
  • form.elements

    表单元素可以通过 form.elements[name/index] 的方式访问,或者也可以使用 form[name/index]elements 属性也适用于 <fieldset>

  • element.form

    元素通过 form 属性来引用它们所属的表单。

value 可以被通过 input.valuetextarea.valueselect.value 等来获取到。(对于单选按钮(radio button)和复选框(checkbox),可以使用 input.checked 来确定是否选择了一个值。

对于 <select>,我们可以通过索引 select.selectedIndex 来获取它的 value,也可以通过 <option> 集合 select.options 来获取它的 value

一个 <select> 元素有 3 个重要的属性:

  1. select.options —— <option> 的子元素的集合,
  2. select.value —— 当前所选择的 <option>value
  3. select.selectedIndex —— 当前所选择的 <option>编号

radio:

<script>
function getRadioValue(){
    var radio = document.querySelector('input[name="sex"]:checked');
    alert("您选择的是:" + radio.value);
}
</script>
<body>
    <input type="radio" name="sex" value="男" checked><input type="radio" name="sex" value="女"><br />
    <input type="button" value="确定" onclick="getRadioValue()">
</body>
focus/ blur

在元素获得/失去焦点时会触发 focusblur 事件。

它们的特点是:

  • 它们不会冒泡。但是可以改为在捕获阶段触发,或者使用 focusin/focusout

    可以使用 focusinfocusout 事件 —— 与 focus/blur 事件完全一样,只是它们会冒泡。值得注意的是,必须使用 elem.addEventListener 来分配它们,而不是 on<event>

  • 大多数元素默认不支持聚焦。使用 tabindex 可以使任何元素变成可聚焦的。

可以通过 document.activeElement 来获取当前所聚焦的元素。

事件更改

在这里插入图片描述

表单提交
  1. 事件submit(Enter也会触发,同时调用一次click事件)

    <form onsubmit="alert('submit!');return false">
      First: Enter in the input field <input type="text" value="text"><br>
      Second: Click "submit": <input type="submit" value="Submit">
    </form>
    
  2. 方法submit

    let form = document.createElement('form');
    form.action = 'https://google.com/search';
    form.method = 'GET';
    
    form.innerHTML = '<input name="q" value="test">';
    
    // 该表单必须在文档中才能提交
    document.body.append(form);
    
    form.submit();
    

heckbox),可以使用 input.checked 来确定是否选择了一个值。

对于 <select>,我们可以通过索引 select.selectedIndex 来获取它的 value,也可以通过 <option> 集合 select.options 来获取它的 value

一个 <select> 元素有 3 个重要的属性:

  1. select.options —— <option> 的子元素的集合,
  2. select.value —— 当前所选择的 <option>value
  3. select.selectedIndex —— 当前所选择的 <option>编号

radio:

<script>
function getRadioValue(){
    var radio = document.querySelector('input[name="sex"]:checked');
    alert("您选择的是:" + radio.value);
}
</script>
<body>
    <input type="radio" name="sex" value="男" checked><input type="radio" name="sex" value="女"><br />
    <input type="button" value="确定" onclick="getRadioValue()">
</body>
focus/ blur

在元素获得/失去焦点时会触发 focusblur 事件。

它们的特点是:

  • 它们不会冒泡。但是可以改为在捕获阶段触发,或者使用 focusin/focusout

    可以使用 focusinfocusout 事件 —— 与 focus/blur 事件完全一样,只是它们会冒泡。值得注意的是,必须使用 elem.addEventListener 来分配它们,而不是 on<event>

  • 大多数元素默认不支持聚焦。使用 tabindex 可以使任何元素变成可聚焦的。

可以通过 document.activeElement 来获取当前所聚焦的元素。

事件更改

[外链图片转存中…(img-WbYdbrI0-1710555725311)]

表单提交
  1. 事件submit(Enter也会触发,同时调用一次click事件)

    <form onsubmit="alert('submit!');return false">
      First: Enter in the input field <input type="text" value="text"><br>
      Second: Click "submit": <input type="submit" value="Submit">
    </form>
    
  2. 方法submit

    let form = document.createElement('form');
    form.action = 'https://google.com/search';
    form.method = 'GET';
    
    form.innerHTML = '<input name="q" value="test">';
    
    // 该表单必须在文档中才能提交
    document.body.append(form);
    
    form.submit();
    
  • 17
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值