Web tips

2.21'23

Smooth scroll

const scrollDown = () => {
  window.scrollBy({
    top: 20, 
  })
  if (keepScroll) {
    window.requestAnimationFrame(scrollDown)
  }
}

Request header

  • Host 请求的目标服务器域名。未指定端口时,默认443(https) 或80(http)

    Host: developer.mozilla.org
    
  • Origin 发送请求的源域名

    Origin: https://developer.mozilla.org
    
  • Referer 发送请求的页面地址(完整或部分地址)

    Referer: https://developer.mozilla.org/en-US/docs/Web/JavaScript
    Referer: https://example.com/page?q=123
    Referer: https://example.com/
    
  • Location 页面重定向的页面地址(绝对或相对地址)。仅用于响应状态为3xx201

    Location: /index.html
    

Use emoji as favicon

<link rel="icon" href="data:image/svg+xml,<svg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 100 100%22><text y=%22.9em%22 font-size=%2290%22>⌛</text></svg>">

Audio

Proxy

Proxy 用来为代理其它对象,便于控制对该对象的读写等基础操作

new Proxy(target, handler)
const target = {
  message1: 'Hello',
  message2: 'everyone'
}

const handler = {
  get: function (target, prop, receiver) {
    return 'world'
  }
}

const proxy = new Proxy(target, handler)

console.log(proxy.message1) // world

history.pushState

可以手动添加历史记录和状态,用于浏览器的前进和后退

history.pushState(state, title, [, url])

Get selected text

window.getSelection().toString()

动态添加脚本、样式等

const script = document.createElement("script");
script.setAttribute("src", "https://example.com/example.js");
document.body.appendChild(script);

const style = document.createElement("link");
style.setAttribute("href", "https://example.com/example.css");
document.body.appendChild(style);

可折叠元素

<summary>: The Disclosure Summary element

I have keys but no doors. I have space but no room. You can enter but can’t leave. What am I? A keyboard.
<details>
  <summary
    >I have keys but no doors. I have space but no room. You can enter but can’t
    leave. What am I?</summary
  >
  A keyboard.
</details>

元素到顶部的距离

offsetTop -> offsetParent

HTMLElement.offsetTop: 元素到父元素(offsetParent)的距离。一般是固定值。

topPos = element.offsetTop;

getBoundingClientRect().top -> viewport

getBoundingClientRect().top 获取元素到 viewport 的距离。随页面滚动而变化。

domRectTop = element.getBoundingClientRect().top;

元素高度

窗口高度

window.innerHeight

设置图片高度

imgDom.height = 200;

普通元素高度

dom.style.height = '50px';

点击选中所有文本

function selectText(containerid) {
  if (document.selection) {
    // IE
    var range = document.body.createTextRange();
    range.moveToElementText(document.getElementById(containerid));
    range.select();
  } else if (window.getSelection) {
    var range = document.createRange();
    range.selectNode(document.getElementById(containerid));
    window.getSelection().removeAllRanges();
    window.getSelection().addRange(range);
  }
}

asiidoc 文档

与 markdown 类似的文档书写格式,一个例子:https://github.com/billziss-gh/winfsp/blob/master/doc/WinFsp-Tutorial.asciidoc

文件对象转为 base64 字符串

File 是 Blob 的子类,可以使用 FileReader 读取

const getBase64 = (file) => {
  return new Promise((resolve, reject) => {
    const reader = new FileReader();
    reader.readAsDataURL(file);
    reader.onload = () => {
      resolve(reader.result);
    };
    reader.onerror = (error) => {
      reject(error);
    };
  });
};

使用 rel="noopener"避免target="_blank"导致的安全问题

问题:a标签单独使用target="_blank"属性时,目标网站会获取原网站的权限(window.opener 对象),导致很严重的安全问题。

建议处理方案:使用rel="noopener noreferrer"

<a href="{src}" target="_blank" rel="noopener noreferrer">Out Link</a>

https://mathiasbynens.github.io/rel-noopener 很好地解释了这个问题。

避免表单提交时自动在 URL 上添加问号(?)

假设定义了表单

<form onSubmit={submit}>
    ...
</form>

在提交响应中需要取消浏览器事件的默认行为

submit = (e) => {
  e.preventDefault();
};

DOM 与 string 的相互转换

string to DOM

const parser = new DOMParser();
const dom = parser.parseFromString(text, "text/html");

DOM to string

// whole dom content
const div = document.createElement("div");
console.log(div.outerHTML);

// content inside dom
console.log(node.innerHTML);

WebRTC: 浏览器实时交换数据

WebRTC API

自定义请求 header

注意 key 中尽量不要包含下划线。部分服务器出于安全考虑会去掉这些请求头。

获取事件定义时的 DOM

获取事件定义所在的 DOM

event.currentTarget

获取导致事件触发的 DOM

event.target

滑动条实现:input[type="range"]

<input
  type="range"
  value="1"
  min="0.5"
  max="10"
  className="slide"
  id="myRange"
/>
var slider = document.getElementById("myRange");

slider.oninput = function () {
  this.value;
};

检查如何移动到当前页面的

performance.navigation.type
// 0: link
// 1: reload
// 2: back_forward
// 255: other

Sticky 失效问题

经常是由于父元素包含滚动样式(例如设置了样式 overflow: scroll)导致的。可以取消父元素的滚动,改为监听 window 的滚动。

事件捕获和冒泡

DOM 事件流传递有两种模型,捕获(capture):父元素到子元素;冒泡(bubble):子元素到父元素。现行标准中,由目标元素(子元素)触发的完整的事件流包含先捕获后冒泡两个阶段,即父 ->(捕获) 子 ->(冒泡) 父

使用addEventListener的第三个参数可以指定是否在捕获阶段触发事件(默认冒泡)。如果指定为捕获阶段,可以达到比默认冒泡提前触发响应事件的效果。

使用stopPropagation可以阻止事件继续传递。

最内部的子元素定义多个事件时按注册顺序触发。

<div id="div1">
  div1
  <div id="div2">div2</div>
</div>

<script>
  document.getElementById("div1").addEventListener(
    "click",
    function (e) {
      console.log("click div1 at capture");
    },
    true
  );
  document.getElementById("div2").addEventListener(
    "click",
    function (e) {
      console.log("click div2 at capture");
    },
    true
  );
  document.getElementById("div1").addEventListener("click", function (e) {
    console.log("click div1 at bubble");
  });
  document.getElementById("div2").addEventListener("click", function (e) {
    console.log("click div2 at bubble");
  });
</script>

点击div1后的 console

click div1 at capture
click div2 at capture
click div2 at bubble
click div1 at bubble

table 设置列宽度

设置 table-layout 属性为 fixed

.table-fixed {
  table-layout: fixed;
}

.table-fixed th.small {
  width: 30%;
}

.table-fixed td {
  word-wrap: break-word;
}

显示任意 Unicode

对应编码:U+263A

👏 对应编码:U+1F44F

HTML 直接展示

<span>&#x263A;</span>

JavaScript 处理后展示

// 0xFFFF 以下 (16-bits)
String.fromCharCode(0x263a);

// 所有 Unicode (21 bits)
String.fromCodePoint(0x1f44f);

table-layout

指定 table 中 cells, rows, columns 的布局

auto: cells 的宽度会适应内部的内容 fix: cells 的宽度由第一行的 cells 决定

table {
  table-layout: fixed;
  width: 100px;
}

viewport

将移动设备上浏览器默认宽度设置为屏幕宽度。(默认会大于屏幕宽度)

<meta name="viewport" content="width=device-width, initial-scale=1" />

<a>下载链接

<a href="path/to/file.ext" download="filename">Downalod Link</a>

<ruby> 元素,添加上标

<ruby><rp>(</rp><rt>Han</rt><rp>)</rp><rp>(</rp><rt>zi</rt><rp>)</rp>
</ruby>
(Han)(zi)

input(type=number) 输入小数

input(type="number") 输入类型为数值。可以设置最小最大值。容易被忽视的一点是,通过修改 step 可以输入小数(float)。

下面例子中,允许输入最小 0.1,最大 10,间隔为 0.1 的数值。

<input type="number" step="0.1" min="0" max="10" />
📖