自定义应用程序的自定义元素 – 带有 Mozilla 的 Brick 和 X-Tag 的 Web 组件

在本文中,我们将探讨 Mozilla 的 Brick 和 X-Tag 库的使用。首先,我们将使用 Brick 快速原型化一个简单的应用程序。然后,我们将使用 X-Tag 构建一个自定义 Web 组件。

技术

Brick:精选的 Web 组件

Brick 是一套模块化、可复用的 UI 组件。这些组件专为自适应、响应式应用程序而设计,是采用 移动优先 方法的绝佳选择,而这基本上是 Web 应用程序的开发方式。这种理念及其设计模式适应各种设备。Brick 组件不仅适用于移动应用程序,还适用于现代应用程序

Brick 有点像一个库,但实际上你应该把它看作是精选的 Web 组件集合

Brick 的组件集合在你的 HTML 中以声明式方式使用<like-this>,并且可以像普通非自定义元素一样用 CSS 样式化。Brick 组件拥有自己的微型 API,用于与它们进行交互。这些组件是构建块。你喜欢乐高吗?很好。你会喜欢 Brick 的。

Brick Web 组件使用 X-Tag 自定义元素 polyfill 创建。

什么是 X-Tag?

X-Tag 是一个库,它为浏览器中启用 Web 组件 的几个(很快全部)功能提供 polyfill。特别是,X-Tag 专注于 polyfill 自定义元素的创建,以便你可以使用自己的声明式语法和特定于元素的 API 扩展 DOM。

当你使用来自 Brick 的组件时,你就是在使用使用 X-Tag 库制作的 Web 组件。Brick 包含 X-Tag 核心,因此如果你包含 Brick 然后决定创建自己的自定义元素——你不需要包含 X-Tag 来做到这一点,X-Tag 的所有功能已经为你提供。

下载演示项目文件

下载演示项目文件。首先,我们将使用 使用 Brick 的简单应用程序 文件夹中的材料。

在应用程序中使用 Brick

在这里,我们将使用<x-appbar><x-deck><x-card> 构建一个简单的骨架应用程序。 x-appbar 为我们的应用程序提供了一个整洁的标题栏,而作为 x-deck 子元素的 x-card 为我们提供了具有过渡效果的多个视图。

首先,我们从一个最基本的 HTML 文档开始,然后包含 Brick 的 CSS 和 JS,以及我们自己的应用程序特定代码(在下面的示例中分别为 app.cssapp.js)。


  
    
    
    
    
    Simple - Brick Demo
  
  
    
    
    
  
  

现在我们添加一些 Brick 元素


  
Simple Brick App

在你的 x-appbar 下面,创建一个包含一些 x-card 作为子元素的 x-deck。你可以给 x-card 任何你喜欢的内容。



  
    

View 1

Hello, world!

Pick a Date

<x-datepicker>s are a polyfill for <input type="date">

Just here to show you another tag in action!

A Random Cat To Spice Things Up

我们已经几乎完成了简单应用程序的结构。现在我们只需要一点点 CSS 和 Javascript 来将它连接在一起。

document.addEventListener('DOMComponentsLoaded', function() {
  // Run any code here that depends on Brick components being loaded first
  // Very similar to jQuery's document.ready()

  // Grab the x-deck and the two buttons found in our x-appbar and assign to local vars
  var deck = document.getElementById("views"),
  nextButton = document.getElementById("view-next"),
  prevButton = document.getElementById("view-prev");

  // Add event listeners so that when we click the buttons, our views transition between one another
  prevButton.addEventListener("click", function(){ deck.previousCard(); });
  nextButton.addEventListener("click", function(){ deck.nextCard(); });
});

<x-calendar> example in our demo application

一些简单的 CSS 来样式化我们初具雏形的应用程序

html, body {
  margin: 0;
  padding: 0;
  font-family: sans-serif;
  height: 100%;
}

h1 {
  font-size: 100%;
}

x-deck > x-card {
  background: #eee;
  padding: 0.6em
}

Ka-bam!用一点点声明式标记和一些调整,我们就得到了一个骨架,任何人都可以使用它在单个 HTML 文档中创建多视图应用程序。如果你在开发者工具中查看标记,你会看到 Brick 自定义元素与原生 HTML 元素和谐共处——你可以像操作普通 HTML 一样使用开发者工具来检查和操作它们。

Custom elements alongside HTML elements in the Firefox Developer Tools

现在让我们学习如何使用 X-Tag 创建自己的自定义元素。

使用 X-Tag 创建自定义元素(你自己的“Brick”)

假设我们有一个移动应用程序,用户在其中执行会导致阻塞任务的操作。也许应用程序正在等待外部服务。程序完成用户启动的任务的下一个指令取决于服务器的数据,因此不幸的是我们必须等待。为了我们这里目的,让我们假设我们不能过多地修改我们的程序,并假设一个根深蒂固的体系结构——也许除了与用户沟通直到我们找到更好的处理阻塞方式之外,我们不能做太多其他事情。我们必须尽力而为。

我们将创建一个自定义模态微调器,它会告诉用户等待。当用户无法及时完成任务时,向他们提供应用程序中正在发生的事情的反馈非常重要。一个沮丧或困惑的用户可能会放弃使用你的应用程序。

现在,你需要切换到演示材料中的 x-status-hud 文件夹。

1. 注册你的自定义元素

X-Tag 依赖于几个不同的事件来检测和将元素升级为自定义元素。无论元素是在原始源文档中存在,通过设置 innerHTML 属性添加,还是通过document.createElement动态创建,X-Tag 都能正常工作。你应该查看 X-Tag 文档的 帮助程序部分,因为它涵盖了各种功能,允许你像使用原生元素一样使用你的自定义元素。

我们需要做的第一件事是注册我们的自定义元素到 X-Tag,以便 X-Tag 了解何时遇到我们的自定义元素时该做什么。我们通过调用xtag.register 来实现。

xtag.register('x-status-hud', {
  // We will give our tag custom behavior here for our status indicating spinner
});

!重要:所有自定义元素名称必须包含一个连字符。为什么?这里的想法是,没有标准的 HTML 元素包含连字符,因此我们不会践踏现有的命名空间并发生冲突。你不必使用‘x-‘作为前缀,这只是在 Brick 生态系统中使用 X-Tag 创建的组件时使用的约定。在 W3C 自定义元素规范的早期,人们推测所有自定义元素都将使用 x- 作为前缀;这个限制在规范的后续版本中被放宽了。如果你要命名一个元素为‘bacon-eggs’ 或‘adorable-kitten’,这两个都是完全有效的名称。选择一个描述你的元素是什么做什么的名称。

如果我们愿意,可以选择在升级之前设置用作基础元素的 HTML 元素。如果我们想要包含来自不同元素的功能,我们还可以设置一个特定的原型。你可以按如下方式声明它们

xtag.register('x-superinput', {
  extends: 'input',
  prototype: Object.create(HTMLInputElement.prototype)
});

我们正在构建的元素不需要显式地设置这些属性。提它们是因为当你编写更高级的组件并且想要对它们进行特定程度的控制时,它们将对你有用。

2. 元素生命周期

自定义元素在它们生命周期中的某些时间会触发事件。当元素创建、插入 DOM、从 DOM 中移除以及设置属性时,会触发事件。你可以利用其中零个或所有事件。

lifecycle:{
  created: function(){
    // fired once at the time a component
    // is initially created or parsed
  },
  inserted: function(){
    // fired each time a component
    // is inserted into the DOM
  },
  removed: function(){
    // fired each time an element
    // is removed from DOM
  },
  attributeChanged: function(){
    // fired when attributes are set
  }

我们的元素将使用created 事件。当此事件触发时,我们的代码将添加一些子元素。

xtag.register('x-status-hud', {
  lifecycle: {
    created: function(){
        this.xtag.textEl = document.createElement('strong');

        this.xtag.spinnerContainer = document.createElement('div');
        this.xtag.spinner = document.createElement('div');

        this.xtag.spinnerContainer.className = 'spinner';

        this.xtag.spinnerContainer.appendChild(this.xtag.spinner);
        this.appendChild(this.xtag.spinnerContainer);
        this.appendChild(this.xtag.textEl);
    }
  }
  // More configuration of our element will follow here
});

3. 添加自定义方法

我们需要控制何时显示或隐藏我们的状态 HUD。为此,我们需要向我们的组件添加一些方法。让我们添加一些函数来做到这一点。对于某些用例,简单的 toggle() 可能就足够了,但让我们也加入单独的 hide()show() 函数。

xtag.register('x-status-hud', {
  lifecycle: {
    created: function(){
      this.xtag.textEl = document.createElement('strong');

      this.xtag.spinnerContainer = document.createElement('div');
      this.xtag.spinner = document.createElement('div');

      this.xtag.spinnerContainer.className = 'spinner';

      this.xtag.spinnerContainer.appendChild(this.xtag.spinner);
      this.appendChild(this.xtag.spinnerContainer);
      this.appendChild(this.xtag.textEl);
    }
  },

  methods: {
    toggle: function(){
      this.visible = this.visible ? false : true;
    },

    show: function (){
      this.visible = true;
    },

    hide: function (){
      this.visible = false;
    }
  }

4. 添加自定义访问器

关于自定义元素上的属性,需要注意的一点是:它们不必映射到属性。这是设计使然,因为某些设置器可能非常复杂,没有合理的属性等效项。

如果你希望属性和属性相关联,将一个空对象字面量传递给 attribute。你将在下面看到,这在 label 属性中是如何实现的。

xtag.register('x-status-hud', {
  lifecycle: {
    created: function(){
      this.xtag.textEl = document.createElement('strong');

      this.xtag.spinnerContainer = document.createElement('div');
      this.xtag.spinner = document.createElement('div');

      this.xtag.spinnerContainer.className = 'spinner';

      this.xtag.spinnerContainer.appendChild(this.xtag.spinner);
      this.appendChild(this.xtag.spinnerContainer);
      this.appendChild(this.xtag.textEl);
    }
  },

  methods: {
    toggle: function(){
      this.visible = this.visible ? false : true;
    },

    show: function (){
      this.visible = true;
    },

    hide: function (){
      this.visible = false;
    }
  },

  accessors: {
    visible: {
      attribute: { boolean: true }
    },

    label: {
      attribute: {},

      set: function(text) {
        this.xtag.textEl.innerHTML = text;
      }
    }
  }
}); // End tag declaration

如果你不清楚属性和属性之间的区别,查看这个问题的 Stack Overflow 答案。虽然提出的问题完全是关于其他东西(jQuery),但最上面的答案有一个很好的解释,可以帮助你理解属性和属性之间的关系。

完成的组件

当我们编写依赖于自定义元素已经加载的代码时,我们会添加一个事件监听器,该监听器会在组件加载完成后触发。这有点像 jQuery 的 document.ready


X-Tag Status HUD Demo

就是这样。我们已经为我们的客户端代码创建了一个简单的模块化、可复用的窗口小部件。这是一个很好的起点。但它真的完成了吗?

一些我们可以改进此元素的方法

  • attributeChanged 事件触发时,让元素重新计算其大小,并让组件调整大小以适应标签,而不是用省略号截断标签
  • 让开发人员设置一个图像,例如一个动画 GIF,来代替 CSS 微调器,以进一步定制用户体验
  • 使用进度条代替微调器,向用户提供有关任务进度的更多信息

发挥你的创造力,想出一些超出这些内容的小而实用的功能和改进,作为你自己的练习。

完成本教程后,你应该了解如何使用自己的自定义元素扩展 DOM。如果你卡住了,请留言,我们将尽力帮助你回到正轨。如果你没有卡住,请发布你的 GitHub 仓库,向我们展示你用 Brick 和 X-Tag 创建了什么。祝你编程愉快!

关于 Angelina Fabbro

我是一位来自加拿大不列颠哥伦比亚省温哥华的开发者,在 Mozilla 工作,担任 Firefox OS 的技术布道者和开发者倡导者。我喜欢 JavaScript、Web Components、Node.js、移动应用开发,以及我经常光顾的这个很棒的地方——万维网。哦,还有不能忘记 Firefox OS。在我的业余时间,我会上唱歌课,玩万智牌,教别人编程,并与科学家合作,促进程序员与科学家之间的互动。

更多 Angelina Fabbro 的文章...


4 条评论

  1. Felipe N. Moura

    bricks 非常有用,而且易于使用!
    我期待着每一个浏览器都支持 webcomponents 的那一天……但在那之前……

    2014 年 3 月 5 日 上午 5:26

  2. Luke

    看起来不错!这是否包括 hbox、vbox 标签以及像 flex=1 这样的属性,它们在 XUL 中创建动态尺寸的布局?

    如果能够以这种方式将 XUL 的优点添加到 HTML 中,那将很棒,这样开发者就可以使用更自然的 XUL 风格的标记,而不是使用带有百分比高度/像素高度、相对和绝对、浮动以及其他 CSS 技巧来实现布局的元素。

    2014 年 3 月 8 日 下午 9:12

  3. Aravind Mohan

    看起来不错……现在正在使用 Polymer。有机会也会试试这个……

    2014 年 3 月 11 日 下午 5:57

  4. Brett Zamir

    我可能要指出,通过使用 http://www.jsonml.org/https://github.com/brettz9/jamilih,您应该能够获得 Web Components/Brick/X-Tag 的声明性优势,而不会有 HTML 结束标签的丑陋,并且拥有纯粹的 JavaScript(或者如果您需要其严格性,则使用 JSON)的美丽。

    2014 年 3 月 11 日 下午 8:01

本文章的评论已关闭。