乐趣区

关于前端:前端装逼技巧-108-式四-一起摇摆

《洗 练》

中午不眠,
推门至院落,
院中的莲雾树熟了,
有一枚红果悄悄坠落,
我剪一段月光裹住心伤。

七月的虫声是炸了线的唐诗三百,
格律皆破,
独独押一个锡韵:寂寂寂寂寂寂。
我说:渔人哪,你竟不如一只虫子,你三年未归。

瀚海无路,
只有等字,
你无妨托星月当信差,
若我裁得一截银白的咸布,
渍痛了伤口,
我便通晓,
你已无奈回來。

  关注本系列的敌人们,因为一些事件,好久不见了,真是道歉,八月伊始,本系列又回来了。其实我始终在思考,《前端装逼技巧 108 式》的意义到底在哪里?是带来一些开发技巧吗?是整合一些大家疏忽的细节吗?是真的用来装逼的吗?是更多的专一于技术还是趣味性?这是一个取舍问题,偏技术就会失去肯定的趣味性;偏趣味性又会失去一些深度。我认为,纯技术文章其实是很多的了,我更多的是心愿给程序猿生存带来更多的乐趣,在系列文章余下的几篇中,仍然会力求从各个角度更多的为程序人生削减一些别样的色调。

系列文章公布汇总:

  • 前端装逼技巧 108 式(一)—— 打工人
  • 前端装逼技巧 108 式(二)—— 不讲武德
  • 前端装逼技巧 108 式(三)—— 冇得感情的 API 调用工程师
  • 前端装逼技巧 108 式(四)—— 一起摇晃
  • 浏览器是如何工作的:Chrome V8 让你更懂 JavaScript

文章格调所限,援用材料局部,将在对应大节开端标出。

第五十五式:你见过页面跳舞吗?—— 那 High 动的页面一如那些年我逝去的青春

  还记得魔性的小苹果和抖音吗,作为前端的你,有想过 让页面也 High 起来、舞动起来 吗?

  先看成果:

上面这段代码可间接在控制台执行,略长。可间接跳到本大节末,应用简短 JS 引入的形式进行体验(带音乐成果哟

setTimeout(letDance, 1000);
var bgmSrc =
  'https://nd002723.github.io/carnival/audio/Martin%20Jensen%20-%20Fox%20(Loop%20Remix).mp3';
var cssHref = 'https://nd002723.github.io/carnival/css/high.css';
function letDance() {function loadCss() {
    // 将 css 文件引入页面
    var myCss = document.createElement('link');
    myCss.setAttribute('type', 'text/css');
    myCss.setAttribute('rel', 'stylesheet');
    myCss.setAttribute('href', cssHref); //css 文件地址
    myCss.setAttribute('class', l);
    document.body.appendChild(myCss);
  }

  function h() {var e = document.getElementsByClassName(l);
    for (var t = 0; t < e.length; t++) {document.body.removeChild(e[t]);
    }
  }

  function p() {var e = document.createElement('div');
    e.setAttribute('class', a);
    document.body.appendChild(e);
    setTimeout(function () {document.body.removeChild(e);
    }, 100);
  }

  function getSize(e) {
    // 获取指标的宽高
    return {
      height: e.offsetHeight,
      width: e.offsetWidth,
    };
  }

  function checkSize(i) {
    // 判断指标大小是否符合要求
    var s = getSize(i); // 获取指标的宽高
    return (
      s.height > minHeight &&
      s.height < maxHeight &&
      s.width > minWidth &&
      s.width < maxWidth
    ); // 判断指标是否符合条件
  }

  function m(e) {
    var t = e;
    var n = 0;
    while (!!t) {
      n += t.offsetTop;
      t = t.offsetParent;
    }
    return n;
  }

  function g() {
    var e = document.documentElement;
    if (!!window.innerWidth) {return window.innerHeight;} else if (e && !isNaN(e.clientHeight)) {return e.clientHeight;}
    return 0;
  }

  function y() {if (window.pageYOffset) {return window.pageYOffset;}
    return Math.max(
      document.documentElement.scrollTop,
      document.body.scrollTop
    );
  }

  function E(e) {var t = m(e);
    return t >= w && t <= b + w;
  }

  function setBgm() {
    // 设置音乐
    var e = document.createElement('audio');
    e.setAttribute('class', l);
    e.src = bgmSrc; //bgm 地址
    e.loop = false;
    e.addEventListener(
      'canplay',
      function () {setTimeout(function () {x(k);
        }, 500);
        setTimeout(function () {N();
          p();
          for (var e = 0; e < O.length; e++) {T(O[e]);
          }
        }, 15500);
      },
      true
    );
    e.addEventListener(
      'ended',
      function () {N();
        h();},
      true
    );
    e.innerHTML =
      '<p>If you are reading this, it is because your browser does not support the audio element. We recommend that you get a new browser.</p> <p>';
    document.body.appendChild(e);
    e.play();}

  function x(e) {e.className += '' + s +' ' + o;}

  function T(e) {e.className += '' + s +' ' + u[Math.floor(Math.random() * u.length)];
  }

  function N() {var e = document.getElementsByClassName(s);
    var t = new RegExp('\\b' + s + '\\b');
    for (var n = 0; n < e.length;) {e[n].className = e[n].className.replace(t, '');
    }
  }
  var minHeight = 3; // 最小高度
  var minWidth = 3; // 最小宽度
  var maxHeight = 800; // 最大高度
  var maxWidth = 1400; // 最大宽度
  var s = 'mw-harlem_shake_me';
  var o = 'im_first';
  var u = ['im_drunk', 'im_baked', 'im_trippin', 'im_blown'];
  var a = 'mw-strobe_light';
  var l = 'mw_added_css'; // 最终要移除的 css
  var b = g();
  var w = y();
  var C = document.getElementsByTagName('*');
  var k = null;
  for (var L = 0; L < C.length; L++) {var targetDiv = C[L];
    if (checkSize(targetDiv)) {if (E(targetDiv)) {
        k = targetDiv;
        break;
      }
    }
  }
  if (targetDiv === null) {
    // 如果没找到适合大小的
    console.warn('没能找到适合的大小. 换一个页面试试?.');
    return;
  }

  loadCss(); // 将自定义 css 文件引入页面
  setBgm(); // 增加背景音乐

  var O = [];
  for (var L = 0; L < C.length; L++) {var targetDiv = C[L];
    if (checkSize(targetDiv)) {O.push(targetDiv);
    }
  }

  // 网页整体歪斜成果(这块儿原本是 JQuery 实现的,为了防止引入 JQuery,做了改变。)var style = document.createElement('style');
  style.type = 'text/css';
  try {
    style.appendChild(
      document.createTextNode('body{overflow-x:hidden;transform: rotate(1deg);-webkit-transform: rotate(1deg);-moz-transform: rotate(1deg);-o-transform: rotate(1deg);-ms-transform: rotate(1deg)}'
      )
    );
  } catch (ex) {style.styleSheet.cssText = 'body{background-color:red}'; // 针对 IE
  }
  var head = document.getElementsByTagName('head')[0];
  head.appendChild(style);
}

  或者更简洁一点,在页面 URL 栏或者控制台键入以下代码间接体验:

在浏览器地址栏黏贴以下内容的话,有 三点须要留神 ,一是必须是已有内容的页面;二是如果是通过复制黏贴代码到浏览器地址栏的话,IE 及 Chrome 会主动去掉代码结尾的 javascript:,所以须要手动增加起来能力正确执行,而 Firefox 中尽管不会主动去掉,但它基本就不反对在地址栏运行 JS 代码;三是援用的carnival.js 会依赖JQuery(没有的话也没事,只是页面少了一个歪斜的成果)。

javascript: void (function () {
  var d = document,
    a = 'setAttribute',
    s = d.createElement('script');
  s[a]('tyle', 'text/javascript');
  s[a]('src', 'https://nd002723.github.io/carnival/js/carnival.js');
  d.head.appendChild(s);
})();
  • 一个能让你的网站 high 起来的 js

第五十六式:昔时金阶白玉堂,即今唯见青松在 —— 浏览器隐身模式真的能够隐身吗?

隐身模式与设施惟一标识

  咱们都晓得,浏览器隐身模式能够让他人无奈晓得你都拜访了什么网站和做了什么操作,在隐身模式下,关上的网页和下载的文件不会记录到您的浏览历史记录以及下载历史记录中。在你敞开关上的全副隐身窗口后,零碎会删除所有新 Cookie。然而,隐身模式真的就隐身了吗?屡次通过隐身模式拜访同一网站,就真的无奈辨认你曾经拜访过了吗?如果真是这样,那相对是一个刷 UV(unique visitor)的利器了,尝试了下 集体博客,还真是这样(我应用的是不蒜子),那当前谁的 UV 能有我多啊,😄😄😄,不过,等等,如同有哪里不对 … 蒽,原来我是程序猿啊 🤔,如果产品和数据分析师须要准确的数据呢?无需登陆的页面(如社区文章)须要杜绝隐身模式刷 UV 访问量呢?无需登陆的投票站点须要杜绝隐身模式重复投票点赞呢?嗯,这就不得不引入 设施惟一标识 的概念了。

  在开发场景下,惟一的标识一个设施是一个基本功能,能够领有很多利用场景,比方 软件受权(如何保障你的软件在受权后能力在特定机器上应用)、软件 License,设施标识,设施身份辨认 等。

  如果说要获取设施惟一标识,兴许你会想到相似 IMEI、Android ID、MAC 地址等思路,然而 Android 10 中官网文档 中有以下两个表述:

  • 从 Android 10 开始,利用必须具备 READ_PRIVILEGED_PHONE_STATE 特许权限能力拜访设施的不可重置标识符(蕴含 IMEI 和序列号)。
  • 默认状况下,在搭载 Android 10 或更高版本的设施上,零碎会传输随机调配的 MAC 地址。

  一个电脑可能存在多个网卡,多个 MAC 地址,MAC 地址另外一个更加致命的弱点是,MAC 地址很容易手动更改。

  至于 Android ID,则不具备真正的唯一性,ROOT、刷机、复原出厂设置、不同签名的利用等都会导致获取的 Android ID 产生扭转,并且不同厂商定制的零碎的 BUG 会导致不同的设施可能会产生雷同的 Android ID。

  其余一些获取设施惟一标识的办法,这篇文章有比拟全面的阐述:

获取设施惟一标识(Unique Identifier):Windows 零碎

  而如果回到咱们前端场景下,以上这些办法又多了很多局限性,比方有的须要特许权限,有的须要依赖于原生开发的配合,那么,有没有一种 只须要前端参加,也能够取得不错的准确率的惟一标识计划呢?—— 此时就到了浏览器指纹出场的时候了。

  FingerPrint 即咱们常说的指纹识别,应用手指和拇指前端的纹理按下的纹印来鉴定身份。指纹是甄别身份的一种牢靠的办法,具备唯一性,因为每个人的每个指头上的纹理排列各不相同而且不因发育或年龄而扭转。而浏览器指纹是指仅通过浏览器的各种信息,如 CPU 外围数、显卡信息、零碎字体、屏幕分辨率、浏览器插件等组合成的一个字符串,就 能近乎相对定位一个用户,就算应用浏览器的隐衷窗口模式,也无奈防止

  这是一个被动的辨认形式。也就是说,实践上你拜访了某一个网站,那么这个网站就能辨认到你,尽管不晓得你是谁,但你有一个惟一的指纹,未来无论是 广告投放、精准推送、平安防备,还是其余一些对于隐衷的事件,都十分不便。

  市面上对于隐身模式的辨认,也有一些办法,比方已经无效的通过查看 Chrome 的 FileSystem API 的可用性、依据隐身模式和非隐身模式下,脚本执行和对浏览器文件系统的写入速度差别来判断等。

BrowserLeaks

  长期以来,人们始终认为 IP 地址和 Cookie 是用于在线跟踪人员的惟一牢靠数字指纹。但过了一段时间,当古代网络技术容许感兴趣的组织在他们不知情且无奈防止的状况下应用新办法来辨认和跟踪用户时,事件就失控了。

  BrowserLeaks 就是对于浏览隐衷和网络浏览器指纹的。在这里,你将找到一个 Web 技术平安测试工具库,这些工具将向您展现哪些类型的个人身份数据可能会被泄露,以及如何爱护本人免受此类泄露。这个网站提供了包含 IP 地址、地理位置、Canvas、WebGL、WebRTC、字体等多种类型指纹的查看。

  如果你对其中的技术原理很感兴趣,能够进入 BrowserLeaks,点击对应卡片题目进行查看和理解,比方 HTML5 Canvas Fingerprinting 页面,会给出你的 Canvas 指纹及其唯一性率等信息;也能够通过探讨浏览器指纹这篇文章进行理解。

实现 Canvas Fingerprinting

  Canvas Fingerprinting(Canvas 指纹)基于 Canvas 绘制特定内容的图片,应用 canvas.toDataURL()办法返回该图片内容的 base64 编码字符串。对于 PNG 文件格式,以块 (chunk) 划分,最初一块是一段 32 位的 CRC 校验,提取这段 CRC 校验码便能够用于用户的惟一标识。Canvas 利用 HTML5 canvas API 和 JavaScript 来动静生成你想要的图像。和其它跟踪技术一样,这种办法已被成千上万的网站采纳了,包含咱们熟知的广告畛域。

  上面是 Canvas 指纹的一个简略实现,原理其实比较简单,不了解的中央能够参考正文:

// PHP 中,bin2hex() 函数把 ASCII 字符的字符串转换为十六进制值。字符串可通过应用 pack() 函数再转换回去
// 上面是 PHP 的 bin2hex 的 JavaScript 实现
function bin2hex(s) {
  let n,
    o = '';
  s += '';
  for (let i = 0, l = s.length; i < l; i++) {n = s.charCodeAt(i).toString(16);
    o += n.length < 2 ? '0' + n : n;
  }

  return o;
}

// 获取指纹 UUID
function getUUID(domain) {
  // 创立 <canvas> 元素
  let canvas = document.createElement('canvas');
  // getContext() 办法可返回一个对象,该对象提供了用于在画布上绘图的办法和属性
  let ctx = canvas.getContext('2d');
  // 设置在绘制文本时应用的以后文本基线
  ctx.textBaseline = 'top';
  // 设置文本内容的以后字体属性
  ctx.font = "14px'Arial'";
  // 设置用于填充绘画的色彩、突变或模式
  ctx.fillStyle = '#f60';
  // 绘制 "被填充" 的矩形
  ctx.fillRect(125, 1, 62, 20);
  ctx.fillStyle = '#069';
  // 在画布上绘制 "被填充的" 文本
  ctx.fillText(domain, 2, 15);
  ctx.fillStyle = 'rgba(102, 204, 0, 0.7)';
  ctx.fillText(domain, 4, 17);

  // toDataURL 返回一个蕴含图片展现的 data URI
  let b64 = canvas.toDataURL().replace('data:image/png;base64,', '');
  // atob() 办法用于解码应用 base-64 编码的字符串;base-64 编码应用办法是 btoa(),第十九式中有介绍
  let crc = bin2hex(atob(b64).slice(-16, -12));
  return crc;
}

// 调用时,你能够传入任何你想传的字符串,并不局限于传递 domain,这里只是为了便于辨别
console.log(getUUID('https://www.baidu.com/'));

PHP bin2hex() 函数

  测试结果表明,同一浏览器拜访该域时生成的 CRC 校验码总是不变。能够简略了解为 同样的 HTML Canvas 元素绘制操作,在不同的操作系统不同的浏览器上,产生的图片内容其实是不完全相同的。呈现这种状况可能是有几个起因:

  • 在图片格式上,不同 web 浏览器应用了不同的图形处理引擎、不同的图片导出选项、不同的默认压缩级别等。
  • 在像素级别来看,操作系统各自应用了不同的设置和算法来进行抗锯齿和子像素渲染操作。
  • 即便是雷同的绘图操作,最终产生的图片数据在 hash 层面上仍然是不同的。

FingerprintJS

  FingerprintJS 是一个疾速的浏览器指纹库,纯 JavaScript 实现,没有任何依赖。默认状况下,应用 Murmur Hash 算法返回一个 32 位整数,Hash 函数能够很容易地更换。同时,他也很轻量:开启 gzipped 后只有 843 bytes,匿名辨认网络浏览器的准确率高达 94%

MurmurHash 是一种 非加密型哈希 函数,实用于个别的哈希检索操作。由 Austin Appleby 在 2008 年创造,并呈现了多个变种,都曾经公布到了私有畛域(public domain)。与其它风行的哈希函数相比,对于规律性较强的 key,MurmurHash 的随机散布特色体现更良好。

  FingerprintJS 的应用也比较简单:

import FingerprintJS from '@fingerprintjs/fingerprintjs';

// 利用启动时初始化:Initialize an agent at application startup.
const fpPromise = FingerprintJS.load();

(async () => {
  // Get the visitor identifier when you need it.
  const fp = await fpPromise;
  const result = await fp.get();

  // This is the visitor identifier:
  const visitorId = result.visitorId;
  console.log(visitorId);
})();
  • fingerprintJS 介绍与应用
  • fingerprintJS – GitHub
  • fingerprintJS – 官网文档

  下面这些办法,能取得九成以上意义的惟一浏览器指纹,兴许并不能齐全真的惟一,因为比方重写相干 canvas 办法、应用相似猫头鹰浏览器等非凡浏览器还是会使得相干办法生效,然而技术手段更多时候只是一个通用意义上的解决方案,减少破解的壁垒和老本,我认为反对罕用场景下的开发是足够的了。

猫头鹰浏览器是基于 chromium 代码批改编译的浏览器,从底层对各种 API 做了批改,能够交给用户自定义返回各种数据,比方 Canvas、Webgl、AudioContext、WebRTC、字体、UserAgent、屏幕分辨率、CPU 外围数、内存大小、插件信息、语言等信息,这样就能够完全避免被“生成”惟一用户指纹了。因为在线公司、广告商和开发人员喜爱跟踪您的在线流动和操作,以便为您提供有针对性的广告,通常,大家认为这是进犯用户隐衷的。

  有了惟一的浏览器指纹,咱们就能够在相似统计 UV、点赞、投票的时候,带上相干指纹,天然就能够在极大水平上分别用户是否存在刷票、刷访问量的行为了,不过,浏览器指纹技术终归是把双刃剑,在解决以上问题的同时,难免会给用户带来更多的信息透露困扰。

第五十七式:JavaScript Puzzlers! —— 一个能够拿来装逼的题库

  作为程序员,需要无处不在,bug 无处不在,天然,装逼也能够无处不在。回忆你的每一次面试,是否曾被面试官鄙视或者秀一脸?回忆你和共事的每一次交换,是否曾被共事展现的乖僻面试题或者奇技淫巧搞的无语好受 😓,想秀回去却苦于一时“脑中羞涩”,JavaScript Puzzlers! 就是一个收录的各种表态 JavaScript 题目的网站,有了它,当初妈妈再也不怕我找不到能够装逼的代码 / 题目了。

  咱们无妨先看几道题目:

var min = Math.min(),
  max = Math.max();
min < max; // 答案: false
// 乏味的是, Math.min 不传参数返回 Infinity, Math.max 不传参数返回 -Infinity 😆
// 这个还是绝对容易的
var name = 'World!';
(function () {if (typeof name === 'undefined') {
    var name = 'Jack';
    console.log('Goodbye' + name);
  } else {console.log('Hello' + name);
  }
})();
// 答案:Goodbye Jack
'1 2 3'.replace(/\d/g, parseInt); // 答案: 1, NaN, 3

  不,我是一个前端工程师,工程师的事儿,能叫装逼么?兴许它是没有太大用处的脑筋急转弯,然而这叫 小技巧前面藏着的大智慧,哈哈~

  话说回来,这些变态题目波及的知识点很广,如果你能把握这些题目背地的原理,兴许就会对 JavaScript 的了解有一个不小的晋升,上面将题目奉上,供诸君参阅:

目前因为未知起因,兴许是去装逼答题的太多了吧 😄,JavaScript Puzzlers! 网站已不可拜访,所幸您还能够通过上面两个 GitHub 链接查看具体的题目及解析。

  • 44 个 Javascript 变态题解析 (上)
  • 44 个 Javascript 变态题解析 (下)
  • JavaScript Puzzlers!

第五十八式:牛逼的浏览器地址栏

  作为一名前端开发者,咱们甚至每天与浏览器相伴的工夫比女朋友 / 男朋友(如果有的话 😄)陪伴你的都要久,想想那每一个令人“不是那么期待”的晚上,每一个争分夺秒实现工作的傍晚,只有浏览器和编辑器始终是你忠诚的搭档,那么,你理解浏览器吗?理解那个每天都要输出 http://localhost:3000/ 的地址栏吗?本节带你意识那些对于地址栏的好玩儿的细节。

浏览器地址栏运行 JavaScript 代码

  对,你没看错,在“浏览器地址栏运行 JavaScript 代码”,做法是以 javascript: 结尾后跟要执行的语句。须要留神的是如果是 通过复制、黏贴代码到浏览器地址栏的话,IE 及 Chrome 会主动去掉代码结尾的 javascript:,所以须要手动增加起来能力正确执行。并且,你须要在网站的地址栏运行javascript:,而不能在新关上的空标签页面上运行,那样也不会失效。

// 点击确定后,会接着弹出:孤舟蓑笠翁,独钓寒江雪,点击勾销则不会弹出
javascript: if (confirm('千山鸟飞绝,万径人踪灭'))
  alert('孤舟蓑笠翁,独钓寒江雪');

浏览器地址栏运行 HTML 代码

  如果说在“浏览器地址栏运行 JavaScript 代码”晓得的人还算多的话,在“浏览器地址栏运行 HTML 代码”晓得的人就要少一些了,在非 IE 内核的浏览器地址栏能够间接运行 HTML 代码!在地址栏输出以下代码而后回车运行,会呈现下图所示的页面:

data:text/html,
<h1>Nothing is given, Everything is earned!</h1>

浏览器能够是你的记事本

  这个还是在浏览器地址栏下面做文章,将以下代码复制粘贴到浏览器地址栏,运行后浏览器就变成了一个简略原始的编辑器,话不多说,间接试试吧。

data:text/html,
<html contenteditable></html>

  另外,补充一个万能的浏览器 撤销敞开页 快捷键:MAC OS 下,command + shift + t;Windows:ctrl + shift + t。这个 从新关上被敞开的页面 的快捷键,能够始终按,会顺次复原被敞开的页面哟~,要不,看看小伙伴都干了些什么,看看会不会被打 😜

参考资料:这些鲜为人知的前端冷常识,你都 GET 了吗?

第五十九式:汪峰看了会流泪 —— 我是如何轻松走上百度热搜的?

  “汪峰上头条”始终是娱乐界里的一个梗,每次在微博热搜榜看到汪峰,认为能登顶榜首,不想都会有其余的爆点压制住,不论粉丝怎么致力,汪峰还是迟迟上不了头条!

  最初“帮汪峰上头条”反而成了热搜,甚至被收录入百度百科🐶。

  蒽 🤔,如果汪峰是个前端工程师,那热搜的事儿不是分分钟搞定吗?就算不懂 HTML,只有晓得 HTML5 的 contenteditable 属性,控制台输出一个document.body.contentEditable='true';,热搜还不是轻易改,想要多少有多少 😄。

  蒽 🤔,学了这招,甚至你能够轻松通过热搜向对象告白了 … 不过作为 业余的 web 利用和网站开发人员,咱们怎能满足于此呢?

  同理,也是利用了 HTML5 中的 contenteditable 属性,奇妙的在 body 减少一个可编辑的 style 标签,那这个款式,咱们在页面上都能够批改了,想想就 … 没有太多卵用,哈哈哈 😂,不过话说回来,这个属性在局部富文本编辑器上还是很有用途的。很多富文本编辑器就是基于 contenteditable 实现的,具体能够参考深入浅出 contenteditable 富文本编辑器。

<style style="display:block; height:50px;" contenteditable>
  body {background: red;}
</style>

  效果图(图片来自上面的参考文章):

  你认为 contenteditable 只有 truefalse?其实它的可选值包含:

  • contenteditable=””
  • contenteditable=”events”
  • contenteditable=”caret”
  • contenteditable=”plaintext-only”
  • contenteditable=”true”
  • contenteditable=”false”

  除了 HTML5 的 contenteditable 属性,其实还有一个不罕用的 css 属性 —— user-modify能够实现相似的成果,user-modify可取值为以下四个:

  • read-only
  • read-write
  • write-only
  • read-write-plaintext-only

  其中,write-only不必在意,当下这个年代,基本上没有浏览器反对,当前预计也不会有。read-only示意只读,就是一般元素的默认状态。而后,read-writeread-write-plaintext-only 会让元素体现得像个文本域一样,能够 focus 以及输出内容,前者能够输出富文本,而后者只能输出纯文本。

  具体成果你能够通过审查元素,给元素增加 CSS 款式查看,也能够间接看张鑫旭大佬的 CSS user-modify 属性行为表现测试实例页面 demo。

  • 这些鲜为人知的前端冷常识,你都 GET 了吗?
  • 小 tip: 如何让 contenteditable 元素只能输出纯文本

第六十式:蓦然回首,那人却在,灯火阑珊处 —— 我的鼠标去哪儿了

  这个一个暗藏页面上鼠标的技巧,其实不值一提,也没有什么卵用。能够间接复制以下代码到控制台试试(此时如果在页面上吊起右键菜单,还是能够看见鼠标的):

var style = document.createElement('style');
document.head.appendChild(style);

style.type = 'text/css';
style.styleSheet
  ? (style.styleSheet.cssText = '* { cursor: none;!important;}')
  : style.appendChild(document.createTextNode('* { cursor: none;!important;}')
    );

  原理非常简单,设置 cursor 属性为 none 即可:

* {cursor: none !important;}

  或者在浏览器地址栏输出以下内容:

IE 和 Chrome 会主动隐去后面的 javascript: 而后把前面的局部当做查问字段。你须要复制以下代码黏贴后手动在后面加上 javascript:,而后回车成果就进去了。

javascript:function play(){var style = document.createElement('style');document.head.appendChild(style);style.type = 'text/css';style.styleSheet ? (style.styleSheet.cssText = '* { cursor: none !important;}') : style.appendChild(document.createTextNode('* { cursor: none !important;}'));}play();

参考资料:前端轻易玩儿

第六十一式:千呼万唤始进去,犹抱琵琶半遮面 —— 让你的网站模糊不清

  兴许你常常碰到这样的页面,当没有登录的时候,只能看到上面的成果:

  当然,这里的模糊不清成果是通过背景图占位来做的,其实如果不思考安全性、被破解等因素,咱们也齐全能够应用 css 来实现相似的成果。

javascript:function play(){var style = document.createElement('style');document.head.appendChild(style);style.type = 'text/css';style.styleSheet ? (style.styleSheet.cssText = '* { color: transparent !important; text-shadow: #333 0 0 10px !important;}') : style.appendChild(document.createTextNode('* { color: transparent !important; text-shadow: #333 0 0 10px !important;}'));}play();

  不言而喻,这里次要是应用了以下两个 CSS 属性:

color: transparent !important;
text-shadow: #333 0 0 10px !important;

参考资料:前端轻易玩儿

第六十二式:网站如何屏蔽开发者工具

  浏览器开发者工具是给咱们这些 业余的 web 利用和网站开发人员 应用的工具(当然,到底专不业余,本人心里都会有点 B 数 😄),它的作用在于,帮忙开发人员审查元素、对网页进行布局、帮忙前端工程师更好的断点调试等,还能够应用工具查看网页加载过程,进行性能剖析和优化,获取网页申请等(这个过程也叫做抓包)。笔者能够豪不违心的说,来到了开发者工具,炽热而乏味的的前端开发将变得索然无味 ,因为我感触不到有比浏览器自带开发者工具更趁手的利器。当然, 据说真正的大神写出的 JS 和 CSS 都是不须要进行调试的,那咱们另当别论,显然我和真正的大神不是一类人。

  既然开发者工具这么可恶、这么好用,那咱们为什么要屏蔽它呢?—— 可能是因为咱们用过了、实现了开发工作,不想让他人有机会发现那些咱们本人也看不懂的代码以及其中蕴含的商业秘密吧,哼哼,果然是渣男~

  那么,到底该如何做一个有能力能够 屏蔽开发者工具的渣男 呢?

  兴许根据关上控制台的几种形式,你天然就想到了:

  • 监听 F12;
  • 监听和禁止右键菜单(因为右键菜单里有“查看”选项能够关上控制台);

  然而这样真的就行了吗?不,咱们仍然能够通过浏览器右上角的三个点,找到更多工具中的开发者工具,而后点击关上。

  那咱们该以什么思路解决这一问题呢?网上解法有很多,有些非主流,有些因为浏览器的降级已生效,相干思路及链接会在本大节开端给出,这里只说两个我感觉还不错的办法:

const im = new Image();
Object.defineProperty(im, 'id', {get: function () {
    // 在这里放入你的代码,比方我这里会让他跳到百度
    console.log('Console is opened');
    window.location.href = 'http://www.baidu.com';
  },
});
console.log(im); // 谷歌最新版生效

let num = 0; // 谷歌最新版无效
const devtools = new Date();
devtools.toString = function () {
  num++;
  if (num > 1) {
    // 在这里放入你的代码,比方我这里会让他跳到百度
    console.log('Console is opened');
    // window.location.href = "http://www.baidu.com";
    return Date.prototype.toString.call(devtools); // 返回 devtools 后果(这一步不是必须的)}
};
console.log(devtools);

  以上办法的外围原理在于一点:只有关上控制台,才会执行 console 办法 ,而应用console 打印 Date,会调用DatetoString办法,咱们对 toString 办法做了改写。如果间接注入代码,如console = 1,以上代码将生效

  其余几种思路包含:

  • 监听 F12 或者 shift+ctrl+i 调起开发者工具(无奈避免先关上开发者工具,而后在地址栏输出网址的拜访);
  • 监听和禁止右键菜单(因为右键菜单里有“查看”选项能够关上控制台);
  • 监督窗口大小(实用于未将开发工具设置成独立窗口的状况);
  • 监督 DOM 批改(实用于水印爱护等场景);
  • 利用 debugger 的个性,有限递归。

  除了以上办法,也有诸如 devtools-detector 一类的插件,用来对开发者工具关上的监测问题,在此不过多赘述。

其余参考:

  • 网站如何检测到是否开启开发者工具?
  • JS 检测,禁用浏览器开发者工具之 6 大办法探讨
  • 前端开发中如何在 JS 文件中检测用户浏览器是否关上了调试面板(F12 关上开发者工具)?
  • 网站这样来屏蔽开发者工具,不比监听 MouseDown 难受?
  • JS 禁止关上控制台

第六十三式:双兔傍地走,安能辨我是雄雌 —— 你和我谈性能?那么该如何比照各种写法的性能优劣

  性能、各种写法的优劣 是咱们在日常开发、技术探讨中最常提及和关注的。在写一段代码的时候,很多同学可能都会想要晓得它的性能到底如何,和其余写法比起来哪个更快,但却苦于没有好用的工具,只能手动测试运行工夫,这样 一个是不不便,二是因为样本数太少误差较大 。那么,除了对原理解析这种理论性的货色之外,咱们能够怎么简洁、清晰、高效的比照各种不同 JS 写法的执行速度和性能呢?这就波及到 JS 性能测试工具 了。

  JS 性能测试工具原理个别是将给定的测试用例循环在指定环境下运行许多次,而后输入比对后果。JSBench.Me 就是这样一款在线代码性能测试利器。

  同时也有一款 npm 插件 —— 弱小的基准测试库 Benchmark.js 官网说:

Benchmark.js 是一个弱小的 基准测试 库,反对高分辨率计时器并返回具备统计意义的后果。正如在 jsPerf 上看到的那样。

  上文提到的 jsPerf 原本是我想要介绍的一个工具,奈何这款工具有情的回绝了我 😭。

  所以,咱们还是看看 Benchmark.js 这个库的应用吧:

var suite = new Benchmark.Suite();

// add tests
suite
  .add('RegExp#test', function () {/o/.test('Hello World!');
  })
  .add('String#indexOf', function () {'Hello World!'.indexOf('o') > -1;
  })
  // add listeners
  .on('cycle', function (event) {console.log(String(event.target));
  })
  .on('complete', function () {console.log('Fastest is' + this.filter('fastest').map('name'));
  })
  // run async
  .run({async: true});

// logs:
// => RegExp#test x 4,161,532 +-0.99% (59 cycles)
// => String#indexOf x 6,139,623 +-1.00% (131 cycles)
// => Fastest is String#indexOf

第六十四式:requestIdleCallback —— 浏览器一帧都干些什么,也会摸鱼 🐟 吗?

浏览器一帧都会干些什么?

  咱们都晓得,页面的内容都是一帧一帧绘制进去的,浏览器刷新率代表浏览器一秒绘制多少帧。原则上说 1s 内绘制的帧数也多,画面体现就也细腻。

电影放映的规范是每秒放映 24 帧,也就是说电影每秒放映 24 幅画面,以达到动画的成果,超过 24 帧 /s 间断的变动,视觉暂留就会将动态的画“动”起来。钻研表明,人眼接受的极限为每秒 55 帧,还有钻研表明,每秒 60 帧以上能够显著晋升观众的观影感触。每秒 120 帧是每秒 24 帧的 5 倍,采纳这样的拍摄技术能够让画面更加栩栩如生,让观众好像置身其中,给人一种似真似幻的感觉。

  目前浏览器大多是 60Hz(60 帧 /s),每一帧耗时也就是在 16.6ms 左右。那么在这一帧的(16.6ms)过程中浏览器又干了些什么呢?

  通过下面这张图能够分明的晓得,浏览器一帧会通过上面这几个过程:

  1. 承受输出事件,解决用户的交互,如点击、触碰、滚动等事件
  2. 执行事件回调
  3. 开始一帧
  4. 执行 RAF (RequestAnimationFrame)
  5. 页面布局,款式计算
  6. 绘制渲染
  7. 执行 RIC (RequestIdelCallback)

  第七步的 RIC 事件不是每一帧完结都会执行,只有在一帧的 16.6ms 中做完了后面 6 件事儿且还有剩余时间,才会执行。如果一帧执行完结后还有工夫执行 RIC 事件,那么下一帧须要在事件执行完结能力持续渲染,所以 RIC 执行不要超过 30ms,如果长时间不将控制权交还给浏览器,会影响下一帧的渲染,导致页面呈现卡顿和事件响应不及时。

requestIdleCallback 的启发

  咱们以浏览器是否有剩余时间作为工作中断的规范,那么咱们须要一种机制,当浏览器有剩余时间时告诉咱们。

requestIdleCallback((deadline) => {
  // deadline 有两个参数
  // timeRemaining(): 以后帧还剩下多少工夫,最大值 50ms
  // didTimeout: 是否超时
  // 另外 requestIdleCallback 后如果跟上第二个参数 {timeout: ...} 则会强制浏览器在以后帧执行完后执行。if (deadline.timeRemaining() > 0) {// TODO} else {requestIdleCallback(otherTasks);
  }
});
// 用法示例
let tasksNum = 10000;

requestIdleCallback(unImportWork);

function unImportWork(deadline) {while (deadline.timeRemaining() && tasksNum > 0) {console.log(` 执行了 ${10000 - tasksNum + 1}个工作 `);
    tasksNum--;
  }
  if (tasksNum > 0) {
    // 在将来的帧中继续执行
    requestIdleCallback(unImportWork);
  }
}

  其实局部浏览器曾经实现了这个 API,这就是 requestIdleCallback。然而因为以下因素,Facebook 在 React 的重构降级中,摈弃了 requestIdleCallback 的原生 API,而实现了性能更齐备的 requestIdleCallbackpolyfill,这就是Scheduler。除了在闲暇时触发回调的性能外,Scheduler 还提供了多种调度优先级供工作设置:

  • 浏览器兼容性;
  • 触发频率不稳固,受很多因素影响。比方当咱们的浏览器切换 tab 后,之前 tab 注册的 requestIdleCallback 触发的频率会变得很低。

  requestIdleCallback 的 callback 会在浏览器的闲暇工夫运行,而在 w3c 文档里,闲暇工夫分两种:

  • 在执行一段间断的动画时,将给定帧提交到屏幕与开始解决下一帧之间的工夫,这段时间内属于闲暇工夫。在间断动画和屏幕更新期间,此类闲暇工夫会频繁产生,但通常会十分短(即,如果咱们的屏幕是 60hz(1s 内屏幕刷新 60 次)的设施,小于 16 毫秒),如下图所示。

  • 另外一种闲暇工夫,当用户属于闲暇状态(没有与网页进行任何交互),并且屏幕中也没有动画执行。此时闲暇工夫实践上是有限长的。但为了防止在不可预测的工作(例如解决用户输出)引起用户可察觉的提早,这些闲暇时间段的长度应限度为 最大值 50ms,一旦闲暇期完结,浏览器能够安顿另一个闲暇期。

  也就是说,即便浏览器始终处于闲暇状态的话,deadline.timeRemaining能够失去的最长工夫,也是 50ms,这是 w3c 规范 规定的。一些低优先级的工作可应用 requestIdleCallback 等浏览器不忙的时候来执行,同时因为工夫无限,它所执行的工作应该尽量是可能量化,细分的渺小工作。

50 ms 的最大截止工夫来自一个 RESPONSETIME钻研,该钻研表明,对 100 毫秒内用户输出的响应通常被人类感知为刹时的。将闲暇期限限度为 50 ms 意味着即便用户输出在闲暇工作开始后立刻产生,用户代理仍有残余的 50 ms 工夫来响应用户输出,而不会产生用户可察觉的提早。

  当设施的性能越来越好,浏览器反对的成果越来越炫,浏览器的开发者开始越来越多的思考应用原生 API 来解决一些之前特地占用性能的性能,自从最后的 requestAnimationFrameInsterSectionObserver,到requestIdleCallback,对于前端的未来,充满希望,没错,咱们都会有“光明的将来”,哈哈 😄,对于浏览器的更多细节,能够参考我之前的两篇文章:

  • 浏览器是如何工作的:Chrome V8 让你更懂 JavaScript
  • 47 张图带你走进浏览器的世界

拓展浏览与参考

  • requestIdleCallback- 后台任务调度
  • 浏览器帧原理分析
  • Accurately measuring layout on the web
  • Cooperative Scheduling of Background Tasks

第六十五式:如何生成蕴含大小写字母和数字的随机字符串?

  我的项目中,兴许咱们会遇到须要应用 JS 生成特定长度随机字符串的需要,比方用来做 Hash 值、uuid、随机码等,除了能够借助一些库和插件之外,其实局部场景下,咱们齐全能够自定义函数实现 指定长度随机字符串 的生成。

  简洁版函数只须要两行代码:

/**
 * 生成长度为 len 的蕴含 a -z、A-Z、0- 9 的随机字符串
 */
function generateStr(len = 18) {
  // 一行代码生成 0 -9、A-Z、a-z、总长度为 62 的字符数组
  var arr = [...new Array(62)].map((item, i) =>
    String.fromCharCode(i + (i < 10 ? 0 : i < 36 ? 7 : 13) + 48)
  );
  return [...new Array(len)]
    .map(() => arr[Math.floor(Math.random() * arr.length)])
    .join('');
}
generateStr(18);

  如果放心反复,则能够增加一个 Map 来缓存曾经生成的字符串,每次返回时判断一下:

/**
 * 生成长度为 len 的蕴含 a -z、A-Z、0- 9 的随机字符串
 */
const cacheMap = new Map(); // 缓存曾经生成过了的字符串
// 一行代码生成 0 -9、A-Z、a-z、总长度为 62 的字符数组
const arr = [...new Array(62)].map((item, i) =>
  String.fromCharCode(i + (i < 10 ? 0 : i < 36 ? 7 : 13) + 48)
);
function generateStr(len = 18) {const str = [...new Array(len)]
    .map(() => arr[Math.floor(Math.random() * arr.length)])
    .join('');
  if (cacheMap.has(str)) {
    // 这里会有死循环的问题,比方上面的 for 循环,i 设置的大于 62
    console.log(cacheMap, str);
    // i 值越大,len 越小,反复的概率越大
    return generateStr(len);
  } else {cacheMap.set(str, true);
    return str;
  }
}
for (let i = 0; i < 20; i++) {
  // 长度选小一点,测试 20 次
  // i 设置的大于 62 会呈现死循环,可先算出排列组合数进行预防
  // i 值越大,len 越小,反复的概率越大,执行工夫越长
  generateStr(1);
}
console.log(cacheMap);

  1 行代码生成指定长度数字:这种办法有毛病,低概率会呈现位数有余的问题(起因是 0.00566 * 100000 = 566,会失落后面的 0),不举荐应用。

// len 最多 16,可能呈现
function generateNum(len = 16) {return Math.floor(Math.random() * Math.pow(10, len));
}
  • 2 行代码生成蕴含大小写字母和数字的随机字符串

第六十六式:何故中道废,自遗今日殃 —— 如何在来到页面时发送申请?

  用户卸载网页的时候(敞开浏览器、刷新浏览器或者跳转其余页面时),有时须要向服务器发送一些统计数据;同时,前端在做异样监控、统计页面拜访时长时,也会须要在页面解体、敞开的时候发送申请。很天然的做法是在 unload 事件或 beforeunload 事件的监听函数外面,应用 XMLHttpRequest 对象发送数据。然而,这样做不是很牢靠,因为 XMLHttpRequest 对象是异步发送,很可能在它行将发送的时候,页面和相干资源曾经卸载,会引起 function not found 的谬误,从而导致发送勾销或者发送失败

  解决办法就是 AJAX 通信改成同步发送,即只有发送实现,页面能力卸载。然而,很多浏览器曾经不反对同步的 XMLHttpRequest 对象了 (即 open() 办法的第三个参数为 false):

window.addEventListener('unload', logData, false);

function logData() {var client = new XMLHttpRequest();
  // 第三个参数示意同步发送
  client.open('POST', '/log', false);
  client.setRequestHeader('Content-Type', 'text/plain;charset=UTF-8');
  client.send(analyticsData);
}

  同步通信有几种变通的办法:

  • 一种做法是新建一个 <img> 元素,数据放在 src 属性,作为 URL 的查问字符串,这时浏览器会期待图片加载实现(服务器回应),再进行卸载。
  • 另一种做法是创立一个循环,规定执行工夫为几秒钟,在这几秒钟内把数据收回去,而后再卸载页面。

  通过在 unload 事件处理器中,创立一个图片元素并设置它的 src 属性的办法来提早卸载以保证数据的发送。因为 绝大多数浏览器会提早卸载以保障图片的载入,所以数据能够在卸载事件中发送

const reportData = (url, data) => {let img = document.createElement('img');
  const params = [];
  Object.keys(data).forEach((key) => {params.push(`${key}=${encodeURIComponent(data[key])}`);
  });
  img.onload = () => (img = null);
  img.src = `${url}?${params.join('&')}`;
};

  这些做法的独特问题是,卸载的工夫被硬生生拖长了,前面页面的加载被推延了,用户体验不好。

  而 Navigator.sendBeacon 就是天生用来解决“网页来到时的申请发送”问题的,该办法可用于通过 HTTP 将大量数据 异步 传输到 Web 服务器。能够发现,同样是采纳异步的形式,然而 Navigator.sendBeacon 收回的异步申请是作为浏览器工作执行的,与以后页面是脱钩的 。因而该办法不会阻塞页面卸载流程和提早前面页面的加载。当用户代理胜利把数据退出浏览器 传输队列 时,sendBeacon() 办法将会返回 true,如果受到队列总数、数据大小的限度后,会返回 false。返回 true 后,只是示意进入了发送队列,浏览器会尽力保障发送胜利,但是否胜利了,无奈判断。

  目前 Google Analytics 应用 Navigator.sendBeacon 来上报数据。Navigator.sendBeacon办法承受两个参数,第一个参数是指标服务器的 URL,第二个参数是所要发送的数据(可选),能够是任意类型(字符串、表单对象、二进制对象等等)。这个办法的返回值是一个布尔值,胜利发送数据为 true,否则为 false。该办法发送数据的 HTTP 办法是 POST,能够跨域,相似于表单提交数据。它不能指定回调函数。

window.addEventListener('unload', analytics, false);

function analytics(state) {if (!navigator.sendBeacon) return;

  var URL = 'http://example.com/analytics';
  var data = 'state=' + state + '&location=' + window.location;
  navigator.sendBeacon(URL, data);
}

  sendBeacon办法具备如下特点:

  • 收回的是异步申请,并且是 POST 申请,后端解析参数时,须要留神解决形式;
  • 收回的申请,是放到的浏览器工作队列执行的,脱离了以后页面,所以不会阻塞以后页面的卸载和前面页面的加载过程,用户体验较好;
  • 只能判断出是否放入浏览器工作队列,不能判断是否发送胜利;
  • Beacon API不提供相应的回调,因而后端返回最好省略response body

参考资料

  • Google Analytics added sendBeacon functionality to Universal Analytics JavaScript API
  • Navigator.sendBeacon 无阻塞发送统计数据
  • Navigator.sendBeacon() —— MDN

第六十七式:身无彩凤双飞翼,心有灵犀一点通 —— 如何让 VSCode、浏览器和你心有灵犀,一点就“通”?

  在刚接手比拟大型项目时,兴许你会常常碰到这样的问题:须要批改一个页面,却苦于对我的项目构造不相熟、文件夹构造不标准等,不晓得文件在哪个目录下;要改一个 bug,却难以迅速定位到问题所在文件,这时候你是否空想过,如果能够点击页面上的组件,在 VSCode 中主动跳转到对应文件,并定位到对应行号岂不美哉?

  react-dev-inspector 就是满足你这些空想的梦中女神,这个插件容许用户通过简略的点击间接从浏览器 React 组件跳转到本地 IDE 代码。TA 不仅能满足你的空想,应用起来也是非常简单不便,看完这张动图,懂得都懂 😜:

  如果看完图,你还不释怀的话,无妨当初就先在在线预览体验地址体验一下(在线体验地址里,激活点击跳转的快捷键是四个按键的组合,不过你齐全不必放心,因为这个组合是能够自定义的,你齐全能够改成两个按键的组合)。

  后面说了,应用形式非常简单,只需三步:

  • 首先,保障你的命令行自身能够通过 code 命令关上 VSCode 编辑器,比方code .,用 VSCode 关上以后文件夹下的文件;如果没有配置这个,能够参考以下步骤:

    • 首先关上 VSCode。
    • 应用 command + shift + p (留神 window 下应用 ctrl + shift + p) 而后搜寻 code,抉择 install 'code' command in path
  • 装置 react-dev-inspector,批改babelrc.jswebpack.config.ts文件:
// babelrc.js
export default {
  plugins: [
    // plugin options docs see:
    // https://github.com/zthxxx/react-dev-inspector#inspector-babel-plugin-options
    'react-dev-inspector/plugins/babel',
  ],
};

// webpack.config.ts
import type {Configuration} from 'webpack';
import {launchEditorMiddleware} from 'react-dev-inspector/plugins/webpack';

const config: Configuration = {
  /**
   * [server side] webpack dev server side middleware for launch IDE app
   */
  devServer: {before: (app) => {app.use(launchEditorMiddleware);
    },
  },
};
  • 对我的项目入口文件进行以下批改:
import React from 'react';
import {Inspector, InspectParams} from 'react-dev-inspector';

const InspectorWrapper =
  process.env.NODE_ENV === 'development' ? Inspector : React.Fragment;

export const Layout = () => {
  // ...

  return (
    <InspectorWrapper
      // props docs see:
      // https://github.com/zthxxx/react-dev-inspector#inspector-component-props
      // 这里能够轻易配置快捷键,你能够改成两个按键的组合
      keys={['control', 'shift', 'command', 'c']}
      disableLaunchEditor={false}
      onHoverElement={(params: InspectParams) => {}}
      onClickElement={(params: InspectParams) => {}}
    >
      {/* 这里是你原来的入口组件 jsx*/}
      <YourComponent>...</YourComponent>
    </InspectorWrapper>
  );
};

  当然,这个插件目前也反对在 Vite2create-react-appUmi 中应用,接入也都很简略,能够参考 react-dev-inspector GitHub 仓库及应用文档。

  这个插件的原理,简略说也分为三步:

  • 构建时 :增加一个 webpack loader 遍历编译前的 AST 节点,在 DOM 节点上加上文件门路、名称等相干的信息。应用 DefinePlugin 注入我的项目运行时的根门路,以便后续用来拼接文件门路,关上 VSCode 相应的文件。
  • 运行时:须要在我的项目的最外层包裹 Inspector 组件,用于在浏览器端监听快捷键,弹出 debug 的遮罩层,在点击遮罩层的时候,利用 fetch 向本机服务发送一个关上 VSCode 的申请。
  • 本地服务:须要启动 react-dev-utils 里的一个中间件,监听一个特定的门路,在本机服务端执行关上 VSCode 的指令,如code src/pages/index.ts

  如果你对其中的原理很感兴趣,能够参考字节跳动 Web Infra 团队的文章——开发提效——我点了页面上的元素,VSCode 乖乖关上了对应的组件?原理揭秘。

第六十八式:摸鱼想藏?何不让浏览器通知大家你正在浏览黄色网站

  下班滑水摸鱼,共事来了连忙切换浏览器界面?何不间接让共事认为你在浏览某些“正经”网站?

  以下代码,当切换浏览器 tab,使得页面不可见时,会更改不可见页面的 title 和 icon 显示成果,能够间接复制以下代码在控制台尝试。

let interval = null;
(function () {
  // 获取 icon 所在 link,rel*="icon" 是为了兼容 rel="shortcut icon" 的状况
  const Link = document.querySelector('link[rel*="icon"]');
  const sourceTitile = document.title;
  const sourceLink = Link.href;
  document.addEventListener('visibilitychange', function () {if (document.hidden) {
      // 让 title 文字动起来,更加醒目
      interval = setInterval(scroll, 1000);
      // 批改 title,这里也能够间接写文字,之所以用编码后的嘛,🤔,是因为不想让你一眼看出代码里下了毒...
      document.title = decodeURI('%E6%82%A8%E6%AD%A3%E5%9C%A8%E6%B5%8F%E8%A7%88%E9%BB%84%E8%89%B2%E7%BD%91%E7%AB%99...');
      Link.href =
        'https://king-hcj.github.io/images/posts/zhuangbility100/nh.gif?raw=true';
      Link.type = 'image/gif';
    } else {clearInterval(interval);
      document.title = sourceTitile;
      Link.href = sourceLink;
    }
  });
})();
function scroll() {
  // 让 title 文字动起来,更加醒目
  const titleInfo = document.title;
  const firstInfo = titleInfo.charAt(0);
  const lastInfo = titleInfo.substring(1, titleInfo.length);
  document.title = lastInfo + firstInfo;
}

  效果图:

第六十九式:带有 Id 属性的元素,会创立全局变量

  对于 HTML 中的 Id 属性,想必您曾经再相熟不过了:

  • id 属性规定 HTML 元素的惟一的 id;
  • id 在 HTML 文档中必须是惟一的;
  • id 属性可用作链接锚(link anchor),通过 JavaScript(HTML DOM)或通过 CSS 为带有指定 id 的元素扭转或增加款式。

  对于 DOM 树中具备 ID 的给定 HTML 元素,能够应用其 ID 作为变量名来检索 div。所以,上面的 console.log(foo) 会打印出 id 为 foo 的这个 div DOM 元素:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <title></title>
  </head>
  <body>
    <div id="foo"></div>

    <script type="text/javascript">
      console.log(foo); // 会输入 id 为 foo 的 div 这个 DOM 元素
    </script>
  </body>
</html>

  如果您不信,那就此时关上您的网站看看:

  那么,这是否也意味着能够在这些浏览器中应用它来代替 getElementById 办法呢?理论我的项目中最好还是老老实实该怎么写就怎么写,毕竟如果这样,大概率你会被起初的接手者或者共事问候的 😄 ~

  • Do DOM tree elements with ids become global variables?

第七十式:利用 a 标签解析 url

  很多时候咱们有从一个 URL 中提取域名,查问关键字,变量参数值等的须要,而万万没想到能够让浏览器不便地帮咱们实现这一工作而不必咱们写正则去抓取。办法是在 JS 代码里先创立一个 a 标签而后将须要解析的 URL 赋值给 a 的 href 属性,而后就失去了所有咱们想要的了。

var a = document.createElement('a');
a.href = 'https://juejin.cn/user/2796746682939054/posts';
console.log(a.host);

  利用这一原理,略微扩大一下,就失去了一个更加强壮的解析 URL 各局部的通用办法了,上面提供一个网上常见的封装示例。

function urlParse(url, key) {var a = document.createElement('a');
  a.href = url;
  var result = {
    href: url,
    protocol: a.protocol.replace(':', ''),
    port: a.port,
    query: a.search,
    params: (function () {var ret = {},
        centArr,
        seg = a.search.replace(/^\?/, '').replace(/^\?/,'').split('&');
      for (i = 0, len = seg.length; i < len; i++) {if (!seg[i]) {continue;}
        centArr = seg[i].split('=');
        ret[centArr[0]] = centArr[1];
      }
      return ret;
    })(),
    hash: a.hash,
    file: (a.pathname.match(/\/([^\/?#]+)$/i) || [, ''])[1],
    path: a.pathname.replace(/^([^\/])/, '/$1'),
    relative: (a.href.match(/tps?:\/\/[^\/]+(.+)/) || [, ''])[1],
    segments: a.pathname.replace(/^\//, '').split('/'),
  };
  a = null;
  return key ? result[key] : result;
}

  H5 有新的 API URL 也能够疾速的解决一个链接,绝对更加简洁。

var url = new URL('https://www.baidu.com/')
url.hash
...
  • 这些鲜为人知的前端冷常识,你都 GET 了吗?

第七十一式:你的在线 IDE —— WebContainers,一个能够在浏览器运行 Node.js 的神器

  在最新的 Google I/O 主题演讲中 stackblitz 向大家介绍了他们与 Next.jsGoogle 团队合作开发的在线 IDE WebContainers

几年前,咱们意识到网络正朝着一个要害的拐点倒退。WebAssembly 的呈现让咱们能够有能力编写基于 WebAssembly 的操作系统,这个操作系统的功能强大到能够齐全在浏览器中运行 Node.js。咱们设计了一个 比本地环境更快,更平安和统一的高级开发环境,能够实现无缝代码合作,而不须要设置本地环境,两年后的明天,他终于诞生了!

  官网宣称 StackBlitz.com 是这个星球上 最快、最平安的开发环境,它的 logo 也是一个闪电的标识:

  WebContainers 容许你创立一个残缺的 Node.js环境,它能够在 毫秒内启动 ,并且能够实现 一键联机和链接共享。这个环境具备 VS Code 弱小的编辑性能,残缺的终端,还有 npm 等性能。它也齐全在你的浏览器中运行,这带来了一些要害的益处:

  • 比本地环境快。构建速度比 yarn/npm 快 20%,包装置速度能够快 5 倍
  • 反对在浏览器中调试 Node.js。与 Chrome DevTools 的无缝集成可实现本机后端调试,无需装置扩大。
  • 默认平安。所有代码执行都产生在浏览器的平安沙箱中,而不是在近程 VM 或本地二进制文件上。

  同样,这些环境不须要在近程服务器上运行。而是每个环境都齐全蕴含在你的 Web 浏览器中。没错:Node.js 运行时自身是第一次在浏览器外部本机运行。你能够在StackBlitz.com 上本人尝试一下,上面是我截取的页面截图:

  • ​ 举荐一个神器!能够在浏览器运行 Node.js
  • 在线 IDE WebContainers 体验地址

第七十二式:如何一起富强、专制、文化、谐和、自在、平等?

  他人写文章都妙笔生花,我上个网,我上个网,鼠标点过的中央都“富强、专制、文化、谐和、自在、平等”,我自豪了吗?

  复制以下代码到控制台执行,而后,开始点击你的页面吧~ 😎

(function () {
  var playWords = [
      '富强',
      '专制',
      '文化',
      '谐和',
      '自在',
      '平等',
      '公正',
      '法制',
      '爱国',
      '敬业',
      '诚信',
      '友善',
    ], // 点击展现的词库
    colors = ['#ff4545', '#3eff00'], // 色彩库
    wordIdx = Math.floor(Math.random() * playWords.length); // 随机取词下标
  document.body.addEventListener('click', function (e) {
    // 监听点击事件
    if (e.target.tagName == 'A') {
      // a 标签
      return;
    }
    var x = e.pageX,
      y = e.pageY, // 获取点击地位
      span = document.createElement('span'); // 创立展现 playWords 的 span

    span.textContent = playWords[wordIdx];
    wordIdx = (wordIdx + 1) % playWords.length;
    color = colors[Math.floor(Math.random() * colors.length)]; // 随机取色
    span.style.cssText = [
      'z-index: 9999; position: absolute; top:',
      y - 20,
      'px; left:',
      x,
      'px; font-weight: bold; color:',
      color,
    ].join('');
    document.body.appendChild(span);
    renderWords(span);
  });

  function renderWords(el) {
    var i = 0,
      top = parseInt(el.style.top);

    var playTimer = setInterval(function () {if (i > 180) {clearInterval(playTimer);
        el.parentNode.removeChild(el);
      } else {
        i += 3;
        el.style.top = top - i + 'px';
        el.style.opacity = (180 - i) / 180;
      }
    }, 16.7);
  }
})();

参考资料:前端轻易玩儿

本文首发于集体博客,欢送斧正和 star。

退出移动版