I learned a lot from a front-end boss article

Recently, I browsed the blog of Mr. Zhang Xinxu and found an article called "JS Parsing and Recognition of Barcode and QR Code with Native Front-End API" . I thought it was very good, so I copied the code of Mr. Zhang and studied it. I was stunned, and my self-confidence was hit. After learning from the pain, I recorded the interesting places in it and compiled it into this article.

Let's take a look at what the page looks like:

chrome-capture-2023-5-26.gif

The function is very simple, just copy the QR code picture below, paste it into the text box, and display it in the middle of the box, and finally click the recognition button to display the result of the QR code recognition below.

source code:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>qrcode</title>
    <style>
      .area {
        height: 200px;
        border: 1px dashed skyblue;
        background-color: #fff;
        display: grid;
        place-items: center;
        margin-top: 20px;
      }
      .area:focus {
        border-style: solid;
      }
      .area:empty::before {
        content: '或粘贴图片到这里';
        color: gray;
      }
      .button {
        margin: 1rem auto;
        width: 160px;
        height: 40px;
        font-size: 112.5%;
        background-color: #eb4646;
        color: #fff;
        border: 0;
        border-radius: 0.25rem;
        margin-top: 1.5rem;
      }
    </style>
  </head>
  <body>
    <div class="container">
      <input id="file" class="file" type="file" accept="image/png" />
      <div id="area" class="area" tabindex="-1"></div>
    </div>
    <p align="center">
      <button id="button" class="button">识别</button>
    </p>

    <p id="result" align="center"></p>

    <p align="center">
      方便大家复制的示意图:<br /><img
        src="./qrcode.png"
        style="margin-top: 10px"
      />
    </p>

    <script>
      var reader = new FileReader()
      reader.onload = function (event) {
        area.innerHTML = '<img src="' + event.target.result + '">'
      }
      document.addEventListener('paste', function (event) {
        var items = event.clipboardData && event.clipboardData.items
        var file = null
        if (items && items.length) {
          // 检索剪切板items
          for (var i = 0; i < items.length; i++) {
            if (items[i].type.indexOf('image') !== -1) {
              file = items[i].getAsFile()
              break
            }
          }
        }
        // 此时file就是剪切板中的图片文件
        if (file) {
          reader.readAsDataURL(file)
        }
      })

      file.addEventListener('change', function (event) {
        const file = event.target.files && event.target.files[0]
        if (file) {
          reader.readAsDataURL(file)
        }
      })

      button.addEventListener('click', function () {
        if ('BarcodeDetector' in window) {
          // 创建检测器
          const barcodeDetector = new BarcodeDetector({
            formats: ['qr_code']
          })

          const eleImg = document.querySelector('#area img')
          if (eleImg) {
            barcodeDetector
              .detect(eleImg)
              .then(barcodes => {
                console.log('barcodes', barcodes)
                barcodes.forEach(barcode => {
                  result.innerHTML = `<span class="success">解析成功,结果是:</span>${barcode.rawValue}`
                })
              })
              .catch(err => {
                result.innerHTML = `<span class="error">解析出错:${err}</span>`
              })
          } else {
            result.innerHTML = `<span class="error">请先粘贴二维码图片</span>`
          }
        } else {
          result.innerHTML = `<span class="error">当前浏览器不支持二维码识别</span>`
        }
      })
    </script>
  </body>
</html>

The background explanation is complete, now let's analyze the subtleties of the code a little bit.

CSSpart

tabindex = -1

<div id="area" class="area" tabindex="-1"></div>

When I saw tabindexthis attribute, I didn't know its usage at all, so I continued to search in Mr. Zhang Xinxu's blog and found an article called "HTML tabindex attribute and web page keyboard accessibility" , here is a brief introduction The usage and function of this attribute.

tabindexis an attribute that is closely related to keyboard access behavior, and it is a global attribute, that is, an attribute that can be used by all HTML tags, such as id, classand other attributes. Therefore, it can be divused on . In addition, this attribute has no compatibility issues, so feel free to use it.

We may not usually feel its value, but once our mouse is broken or out of power, we can only use the keyboard to operate. Or when visiting our webpage on a TV or a projection device, we can only use the buttons on the remote control, which can be seen as the keyboard of the computer. Even if the equipment is completely functional, for advanced users, keyboard access can greatly improve our efficiency.

When an element sets tabindexthe attribute value -1, the element becomes focusable. focusableIt means that the element can be  focusdisplayed by the mouse or JS, and it will have a luminous effect under the chrome browser outline, and it will be a virtual frame under the IE browser, and it can respond to focusevents at the same time.

默认的focusable元素有<a>, <area>, <button>, <input>, <object>, <select> 以及 <textarea>

但是,tabindex = -1不能被键盘的tab键进行focus。这种鼠标可以focus,但是键盘却不能focus的状态,只要tabindex属性值为负值就可以了。

因此,代码中利用了这一特性,设置divfocus的样式,当鼠标点击div时,我们可以改变它的边框,如下:

.area:focus {
    border-style: solid;
 }

tabindex属性值是一个整数,它是用来决定被tabfocus的顺序,顺序越小越先被focus,但是 0除外,如下divfocus的顺序依次是:1,2,3。

<div id="area" class="area" tabindex="1"></div>
<div class="area" tabindex="3"></div>
<div class="area" tabindex="2"></div>

tabindex="0"又是怎么回事呢?

元素设置tabindex="-1",可以鼠标和 JS 可以focus,但键盘不能focus

tabindex="0"tabindex="-1"的唯一区别就是键盘也能focus,但是被focus的顺序是最后的,也就是当你使用tab进行聚焦时,最后才会聚集到tabindex="0"的元素。

<div>设置了tabindex="0",从键盘访问的角度来讲,相对于<div>元素变成了<button>元素。

垂直居中

垂直居中是一个常用的需求了,我经常使用flex来完成:

display: flex;
align-items: center;
justify-content: center;

在大佬的文章中使用了一个新的用法:

display: grid;
place-items: center;

place-items 属性是以下属性的简写:align-itemsjustify-items

这样当粘贴图片时,图片就居中显示了。

:empty::before

div元素没有内容时,.area:empty样式会生效,同时为了显示一段提示内容,使用了伪元素::before,在content写入提示内容。

.area:empty::before {
    content: '或粘贴图片到这里';
    color: gray;
}

这个 css 样式平时用的少,所以这里特意的记录下,以免自己忘记。

JS部分

copy paste 事件

document.addEventListener('paste', function (event) {
    var items = event.clipboardData && event.clipboardData.items
    var file = null
    if (items && items.length) {
      // 检索剪切板items
      for (var i = 0; i < items.length; i++) {
        if (items[i].type.indexOf('image') !== -1) {
          file = items[i].getAsFile()
          break
        }
      }
    }
    // 此时file就是剪切板中的图片文件
    if (file) {
      reader.readAsDataURL(file)
    }
})

这两个事件都属于ClipboardEvent事件(剪切板事件),还有一个cut剪切事件。

wrap.oncopy = function(event){}
wrap.oncut = function(event){}
wrap.onpaste = function(event) {}

目前,大部分软件上的内容都是可以被复制粘贴的,这是因为软件对操作系统复制粘贴操作的实现,软件都会把复制剪切的内容存入操作系统的剪切板上。同样,浏览器也对操作系统的复制剪切板进行了实现,属于浏览器的自身的实现。

浏览器复制剪切的默认行为是触发浏览器的 copy 事件,将 copy 的内容存入操作系统的剪切板中。

那如何干预浏览器的这种默认的复制粘贴操作呢?

可以通过event.preventDefault阻止事件的默认行为,即当触发这三个事件时,阻止对系统剪切板的数据操作。然后,我们对数据进行加工后,重新写入到剪贴板。

比如,当用户复制我们网站的内容时,可以在数据后面加一个版权的相关信息。

<div id="wrap">这是复制的复制内容</div>
    <script>
      var wrap = document.getElementById('wrap')
      wrap.oncopy = function (event) {
        // 通过copy事件监听,阻止将选中内容复制到系统剪切板上
        event.preventDefault() 
        // 获取选中内容对象
        const selection = document.getSelection() 
        // selection对象重构了toSring()方法,获取selection对象的选中内容
        var selectContent = selection.toString() 
        var dealContent =
          selectContent +
          '转载请联系作者,内容地址:xxxxx'
        // 把重写后的内容写入到剪贴板  
        event.clipboardData.setData('text/plain', dealContent)
      }
    </script>

ClipboardEvent 事件有个最重要的属性clipboardData,该属性值是DataTransfer对象,这个对象在拖拽场景中经常使用,后面会专门写一篇文章来说说这个对象。

new BarcodeDetector解析二维码

// 创建检测器
const barcodeDetector = new BarcodeDetector({
    formats: ['qr_code']
})
barcodeDetector.detect(eleImg)
  .then(barcodes => {
    console.log('barcodes', barcodes)
    barcodes.forEach(barcode => {
      result.innerHTML = `<span class="success">解析成功,结果是:</span>${barcode.rawValue}`
    })
  })
  .catch(err => {
    result.innerHTML = `<span class="error">解析出错:${err}</span>`
  })

浏览器提供了原生的API来解析二维码和条形码,即 Barcode Detection API

formats表示要解析那种码,如下图所示:

image.png

这个 API 有一些兼容性的问题,在生成环境中还是尽量使用第三方库。

但是,作为前端开发,也应该时刻关注浏览器的发展,尽早的使用上最新好用的功能。

总结

通过学习上面的代码,可以发现自己在 css,js 方面上的不足,原因是缺乏探索性,老是用已有的知识来解决问题,或者直接去 github 上找第三方库,其实可以使用最简单的方式实现。

Guess you like

Origin juejin.im/post/7248874230862233655