解决 Custom Elements 在 Safari 中的问题

Custom Elements 是 Web Components 中非常重要的一部分,它可以让我们自定义 HTML 元素,并提供自己的行为和样式。但是在 Safari 中,对于一些较为复杂的自定义元素,可能会出现一些问题,比如无法正确实例化、属性无法正常赋值等等。本文将介绍这些问题的原因,并提供解决方案。

问题描述

下面是一个很简单的自定义元素 hello-world,它有一个 name 属性和一个 sayHello 方法。

--------- --------------------------
  -------
    ---------- -
      ----------------- --------
      ------- --- ----- -----
      -------- -----
      ------- ---- --
    -
  --------
  ---- ------------------
    ------ ----- ---------------------
  ------
-----------

--------
  ----- ---------- ------- ----------- -
    ------------- -
      --------
      ----- -------- - ------------------------------------------------
      ----- ----- - ------------------------------------- ------
      ------------------------ ----------------------------
      -------------------------------------------------- - --------------------------
    -
  
    ---------- -
      ------------------- --------------------------------
    -
  -
  
  ------------------------------------------- ------------
---------

在 Chrome 和 Firefox 中,我们可以正确地实例化这个元素,设置属性并调用方法:

----- ------- - --------------------------------------
---------------------------- ---------
-----------------------------------
------------------- -- -- ------- -------

但是在 Safari 中,我们会遇到两个问题:

  1. 在实例化元素时,会抛出一个 Class constructor HelloWorld cannot be invoked without 'new' 的错误。
  2. 在设置属性后,this.getAttribute('name') 返回的始终是 null

这些问题的原因可以归结为 Safari 中 Reflect.construct()CustomElementRegistry.define() 方法的实现存在缺陷。

问题分析

实例化错误

在 Chrome 和 Firefox 中,当我们通过 document.createElement() 创建一个自定义元素时,会得到这个元素的一个实例对象,该对象隐式继承了自定义元素的原型链。而在 Safari 中,由于 Reflect.construct() 方法的实现存在问题,它不会正确地继承原型链,而是把自定义元素的原型和构造函数分别绑定到实例的 __proto__constructor 属性上。这样的结果导致我们无法通过 instanceof 操作符判断一个对象是否是自定义元素的实例。

解决方案是手动绑定原型链。我们可以在自定义元素的构造函数中,设置当前实例的原型为自定义元素的原型:

----- ---------- ------- ----------- -
  ------------- -
    --------
    ----- -------- - ------------------------------------------------
    ----- ----- - ------------------------------------- ------
    ------------------------ ----------------------------
    -------------------------------------------------- - --------------------------
  
    -- -------
    -------------------------------------------------- ----------------------
  -

  -- ---
-

属性赋值错误

在 Chrome 和 Firefox 中,当我们通过 element.setAttribute() 设置元素的属性时,引擎会自动调用自定义元素的 attributeChangedCallback 回调函数,并将 nameoldValue 作为参数传入。而在 Safari 中,CustomElementRegistry.define() 方法的实现存在问题,它在注册自定义元素时没有正确地绑定 attributeChangedCallback,导致在属性赋值时这个回调函数无法触发。

解决方案是手动注册该回调函数。我们可以通过 observeAttributes() 方法手动触发 attributeChangedCallback,并将注册的属性名和回调函数传入:

----- ---------- ------- ----------- -
  ------ --- -------------------- -
    ------ ---------
  -

  ------------- -
    --------
    ----- -------- - ------------------------------------------------
    ----- ----- - ------------------------------------- ------
    ------------------------ ----------------------------
    -------------------------------------------------- - --------------------------
  
    -------------------------------------------------- ----------------------

    -- ----------
    -------------------------------- ------------------------------------------
  -

  ------------------------------ --------- --------- -
    -- ----- --- ------- -
      -------------------------------------------------- - ---------
    -
  -

  ------------------------ --------- -
    ----- ------ - ------------ ----- ------------------ ----- ---------------- -------
    ----- -------- - --- -------------------------- -- -
      -------------------------- -- -
        -- -------------------------------------- --- --- -
          -------------------------------- ------------------ ------------------------------------------------------
        -
      ---
    ---
    ---------------------- --------
  -
-

总结

在 Safari 中,Custom Elements 的实现存在一些缺陷,导致了一些问题。但是我们可以通过手动绑定原型链和手动注册属性变化回调等方式来解决这些问题。通过本文的介绍,我们可以更加深入地理解 Custom Elements 的实现原理,并掌握一些实用的技巧和方法。

完整代码示例:https://codepen.io/anon/pen/eLbZap

来源:JavaScript中文网 ,转载请联系管理员! 本文地址:https://www.javascriptcn.com/post/66432ebdd3423812e4120c38


猜你喜欢

  • 使用 Socket.io 时如何处理跨域问题

    什么是 Socket.io? Socket.io 是一个 JavaScript 库,用于实现实时通信。它包括了 WebSocket、AJAX 长轮询、JSONP 等多种实时通信方式,支持跨平台、跨浏览...

    5 个月前
  • React Router 配置骚操作实现动态加载路由

    React Router 是 React.js 的一个第三方库,用于实现前端路由功能。它可以帮助我们在单页应用中实现多个页面之间的切换,同时可以方便地进行 URL 路由管理。

    5 个月前
  • RxJS 中的 expand 操作符详解

    什么是 RxJS RxJS 是一个 JavaScript 库,它使用可观察序列来编写异步和基于事件的程序。它提供了一种方便的方式来处理异步操作,例如处理用户输入、网络请求和定时器等。

    5 个月前
  • ESLint 如何帮助你规范化代码中的逻辑判断

    在前端开发中,代码的规范化是非常重要的,因为规范化的代码可以提高代码的可读性、可维护性和可扩展性。ESLint 是一个非常流行的 JavaScript 代码检查工具,可以帮助我们规范化代码中的逻辑判断...

    5 个月前
  • 如何在 Chai.js 中进行对象深度相等测试?

    在前端开发中,我们经常需要比较两个对象是否相等。但是,JavaScript 中的对象比较有很多坑,例如对象的属性顺序、对象的类型等等。为了避免这些问题,我们可以使用 Chai.js 中的深度相等测试来...

    5 个月前
  • PWA 中如何实现骨架屏效果

    随着 PWA 技术的发展,越来越多的网站开始采用 PWA 技术来提升用户体验。而骨架屏效果是 PWA 中常用的一种优化手段,可以让用户在等待页面加载时看到页面的基本结构,提升用户体验。

    5 个月前
  • CSS Reset 好还是 Normalize.css 好?

    在前端开发中,我们经常需要处理各种浏览器的兼容性问题。其中一个常见的问题是不同浏览器对 CSS 样式的默认设置不同,导致同一份代码在不同浏览器上的显示效果不同。为了解决这个问题,出现了 CSS Res...

    5 个月前
  • 对 webpack 工程化打包优化的个人总结

    Webpack 是一款非常强大的前端打包工具,它可以将多个文件打包成一个文件,从而提高网站的性能和加载速度。但是,在实际使用中,我们可能会遇到一些性能瓶颈,比如打包速度慢、体积过大等问题。

    5 个月前
  • 为什么 Sequelize 上光查询到 0 条数据呢?

    Sequelize 是一个基于 Node.js 的 ORM(Object-Relational Mapping)框架,它可以让开发者使用 JavaScript 语言操作关系型数据库,如 MySQL、P...

    5 个月前
  • 如何使用 ES11 的 globalThis 对象解决 JS 中的环境问题

    在 JavaScript 中,环境问题是一个非常常见的问题。由于 JavaScript 可以在多个环境中运行,如浏览器、Node.js 等,这导致了一些全局对象(例如 window、global)在不...

    5 个月前
  • Web Components 与 PWA 的实践

    前言 Web Components 是一种新的 Web 技术,它允许开发者创建可重用的自定义 HTML 元素,并将其封装在一个组件中。这种技术可以使 Web 开发更加模块化、灵活和可维护,同时也能提高...

    5 个月前
  • Docker 容器安装及操作详解

    什么是 Docker? Docker 是一种开源的容器化技术,它可以将应用程序及其依赖项打包到一个可移植的容器中,从而实现快速、可靠的应用程序部署。Docker 可以在多种操作系统上运行,包括 Lin...

    5 个月前
  • LESS 常见错误以及解决方法

    LESS 是一种 CSS 预处理器,通过它可以使用变量、嵌套、混合等特性,使得 CSS 更加易于维护和扩展。不过,由于 LESS 本身的语法比较复杂,开发过程中容易出现一些错误。

    5 个月前
  • 如何利用 Golang 开发高性能 RESTful API

    在当今互联网时代,RESTful API 已经成为了开发 Web 应用程序的重要组成部分。而 Golang 作为一门高性能的编程语言,也被越来越多的开发者所关注和使用。

    5 个月前
  • 了解 ES9 中的可选捕获组的语法(Optional Capturing Groups)

    在 ES9(ECMAScript 2018)中,新增了一种语法——可选捕获组(Optional Capturing Groups),它可以在正则表达式中使用,为开发者提供了更加方便快捷的处理字符串的方...

    5 个月前
  • ES10 中的对象半个属性

    在 ES10 中,新增了一种对象半个属性的概念,这种属性可以被赋予一个默认值,但是在访问时需要使用 getter 函数来获取。这种属性可以用来优化对象的性能和灵活性。

    5 个月前
  • 使用 Hapi.js 实现服务器端渲染(SSR)

    随着前端技术的不断发展,单页应用(SPA)越来越受到欢迎。但是,SPA 也存在一些缺陷,比如 SEO 不友好、首屏加载慢等问题。为了解决这些问题,服务器端渲染(SSR)成为了一种流行的解决方案。

    5 个月前
  • 如何使用 Server-sent Events 实现消息推送

    在现代应用程序中,消息推送已成为必不可少的功能。它可以使用户在应用程序处于非活动状态时仍然能够接收到实时更新。Server-sent Events (SSE) 是一种轻量级的协议,用于实现服务器向客户...

    5 个月前
  • 解决 Node.js 中的内存溢出问题

    Node.js 是一个非常流行的服务器端 JavaScript 运行环境,但是在使用 Node.js 开发应用程序时,可能会遇到内存溢出的问题。本文将介绍如何识别和解决 Node.js 中的内存溢出问...

    5 个月前
  • Koa 中路由的处理方法及最佳实践

    Koa 是一个轻量级的 Node.js 框架,它的设计理念是尽可能简洁、高效地处理 HTTP 请求和响应。在 Koa 中,路由是一个非常重要的概念,它决定了如何将不同的请求分发到不同的处理函数中。

    5 个月前

相关推荐

    暂无文章