Drupal 11中利用HTMX创建交互式元素全解析

Drupal 11中利用HTMX创建交互式元素全解析

Drupal 11: Making Interactive Elements With HTMX

15th March 2026 - 40 minutes read time

HTMX是一个JavaScript库,它允许你在不编写任何JavaScript代码的情况下进行ajax调用并创建CSS过渡效果。它通过向HTML元素添加属性来工作,然后利用这些属性设置并执行ajax请求、交换元素以及执行其他一些操作。

它在Drupal 11.3.0*版本中被添加到Drupal中,使开发人员能够使用渲染数组和HTML属性创建交互式元素。其目标是用一个围绕HTMX构建的系统取代整个ajax子系统,要完成这项任务还有很多工作要做。

* 从技术上讲,HTMX从Drupal 11.2.0版本就已经存在于Drupal中,但只是作为一个实验性库。Drupal 11.3.0版本包含了完整的HTMX库以及许多辅助类,使开发变得更加容易。

在本文中,我们将探讨HTMX如何集成到Drupal中,以及有哪些服务可以帮助你在Drupal系统中使用它。

由于本文篇幅较长,我创建了一个目录,以帮助你滚动到相关部分。

首先,让我们简要介绍一下HTMX。

An Introduction To HTMX

HTMX是一个与平台无关的框架,它允许在不编写JavaScript代码的情况下进行ajax调用和CSS过渡。

HTMX库仅16K,相比Drupal多年来一直试图移除的30K的jQuery库来说,它非常小。HTMX没有其他依赖项,所以这16K实际上就是整个库。

所谓与平台无关,是指它不关心背后是什么技术,你可以有一个由HTMX驱动的前端,而后台可以使用几乎任何技术来响应HTTP请求。HTMX向后台发送普通的Web请求,并期望后台以纯HTML响应。Drupal非常擅长生成HTML,因此非常适合作为HTMX使用的后端系统。

HTMX核心有多种功能,同时它还有一组事件钩子,插件可以利用这些钩子添加功能和特性。这意味着如果核心HTMX库不能满足你的需求,你可以扩展它并添加一些代码来满足你的要求。

从根本上说,你可以这样理解HTMX:

- 使用属性为HTML添加行为。
- 任何元素都可以发出Web请求。
- 所有响应都应该是HTML格式。

HTMX属性以 hx-*data-hx-* 为前缀,但使用哪一种都可以。有许多不同的属性可用,但常用的大约只有11个,另外还有20 - 30个属性可以添加更多功能。

例如,让我们看一个不属于Drupal的HTML示例,其中包含几个HTMX属性。

  
    <button hx-get="index.php" hx-target="#div1">Submit</button>
    <div id="div1"></div>
  

上述示例中的属性将执行以下操作。

  • hx-get = 向index.php路径发送一个GET请求。
  • hx-target = 将响应写入ID为 "div1" 的元素中。

对这个请求的响应应该是纯HTML,这意味着我们可以用如下内容进行响应。

  
    <p>Button clicked!</p>
  

这将被注入到我们的div元素中,如下所示。

  
    <div id="div1"><p>Button clicked!</p></div>
  

这基本上就是HTMX的核心功能,其他属性会改变这个请求和替换的核心过程的工作方式。

你可以在我创建的 HTMX with PHP示例仓库 中看到这个示例(以及除此之外的更多示例)。该仓库包含一个HTMX PHP类,它封装了HTMX请求的检测和响应,使示例更容易理解。

在Drupal内部,通过理解几个关键属性,你就可以在HTMX和Drupal方面取得很大进展。请注意,我在这里做了一些简化,但关键概念是存在的。HTMX.org参考页面 也有很好的文档,如需更多信息,请参考该页面。

HTTP Verbs

通过属性可以使用不同的HTTP动词,我们已经看到了 hx-get 属性,但还有其他一些。

  • hx-get 执行 "get" 请求。
  • hx-post 执行 "post" 请求。
  • hx-delete 执行 "delete" 请求。
  • hx-patch 执行 "patch" 请求。
  • hx-put 执行 "put" 请求。

这允许你使用HTML通常不使用的HTTP动词(即不是GET或POST)向服务器发送请求。

hx-target

这指定了要交换的目标元素,通过CSS选择器来定义。

  
    <button hx-get="index.php" hx-target="#div1">Submit</button>
    <div id="div1"></div>
  

hx-trigger

定义将触发请求的操作。对于有默认触发器的元素,这个属性可以省略。链接和按钮已经有 "click" 事件,如果HTMX配置为在这些元素上执行操作,它们会将这些事件传递给HTMX。其他表单元素(如select和input元素)会生成 "change" 事件。

以下是触发器元素以几种不同方式使用的示例。

为div元素添加点击触发器。

  
    <div hx-post="index.php" hx-trigger="click">Click</div>
  

请注意,这不是最佳实践,你可能还应该包含ARIA属性,以使点击操作尽可能具有可访问性。这里的示例侧重于HTMX,但HTML不应孤立存在。

触发点击事件,并添加 once 以仅允许该事件运行一次。这意味着当用户点击它时,按钮在这之后将不会再发出另一个ajax请求。

  
    <button hx-get="index.php" hx-trigger="click once">Click</button>
  

为单个div标签添加加载和点击触发器。这意味着我们可以在元素加载到页面上时触发它加载内容,也可以在用户点击它时触发。

  
    <div hx-post="index.php" hx-trigger="load, click">Load & Click</div>
  

revealed 触发器将在元素在浏览器窗口的视口中可见时才触发事件。

  
    <div hx-post="index.php" hx-trigger="revealed"></div>
  

触发器可以添加延迟,以稍微偏移事件触发的请求。以下代码将在元素加载到页面上时触发对index.php的请求,但仅在500ms延迟之后。

  
    <div hx-post="index.php" hx-trigger="load delay:500ms"></div>
  

HTMX也理解 "去抖动" 的概念。在以下示例中,我们将按键触发延迟1秒,如果用户在这段时间内按下另一个键,则之前的事件将被取消,并创建一个新的事件。这意味着我们不会触发大量事件,只触发序列中的最后一个事件。

  
    <input hx-get="index.php" hx-trigger="keyup delay:1s" />
  

hx-select

此属性将使用CSS选择器从响应中选择要与目标元素交换的内容。

看下面的例子。这里我们有一个 hx-target 属性,它告诉我们结果将放置的位置,但我们还有一个 hx-select 属性。这将用于从响应中选择我们想要注入到目标元素中的元素。

  
    <button hx-get="index.php" hx-select="#response" hx-target="#div1">Click</button>
    <div id="div1"></div>
  

假设我们从服务器得到以下响应。

  
    <p id="response">Button clicked!</p>
    <p>Some extra content that won't get displayed.</p>
  

ID为 "response" 的元素将从响应中被挑选出来并用作内容,其他所有内容将被忽略。

这个属性在Drupal中尤为重要。本文后面会详细解释,当Drupal响应HTMX的请求时,它将返回一个完整的HTML页面,这意味着我们需要使用 hx-select 属性从响应中挑选出我们感兴趣的元素。

hx-select-oob

这里的 "oob" 指的是 "带外" 的概念,这意味着我们将执行与用户当时正在做的事情不一定相关的操作。这个属性允许我们对响应执行两个操作。

看下面的例子。

  
    <button hx-get="index.php" hx-select-oob="#div1">Send Request</button>
    <div id="div1"></div>
  

首先,我们将触发对index.php脚本的GET请求,它可能返回如下内容。

  
    Request Sent
    <div id="div1">Button clicked!</div>
  

按钮的文本将被替换为 "Request Sent",这是请求的第一部分。然后,ID为 "div1" 的元素将从请求中被挑选出来,并用于替换页面中相同的元素。默认情况下,HTMX将使用innerHTML策略 "交换" 元素。重要的是要记住,hx-select-oob 的默认行为是outerHTML,这意味着整个元素将被替换。

hx-select-oob 属性可以接受一个用逗号分隔的元素列表,因此以下示例将更改响应中的两个元素,只要它们在两个地方都存在。

  
    <button hx-get="index.php" hx-select-oob="#div1,#div2">Send Request</button>
    <div id="div1"></div> <div id="div2"></div>
  

hx-swap

这控制元素如何交换到页面中。默认情况下,这是 innerHTML,它将用响应目标替换元素的内容。将其更改为 outerHTML 将替换整个元素。你还可以将其设置为 textContent,这将替换内容而不将其解析为HTML。

其他选项包括:

  • beforebeginafterbeginbeforeendafterend - 将响应放置在元素及其子元素之前或之后。
  • delete - 从页面中删除目标元素。
  • none - 不处理响应(但仍处理带外项)。

hx-swap-oob

交换一个 "带外" 元素。类似于 hx-select-oob,但响应告诉HTMX要将什么交换到页面内容中。

看下面的例子。请注意,我们将 hx-swap 属性设置为 "none",这意味着我们不会对这个元素进行任何交换。

  
    <button hx-get="index.php" hx-swap="none">Send Request</button>
    <div id="div1"></div>
  

服务器的响应包含一个ID为 "div1" 的div,并且 hx-swap-oob 属性设置为true。

  
    <div id="div1" hx-swap-oob="true">Button clicked!</div>
  

这将在页面上找到具有相同ID的元素,并将其替换为这个元素。这实际上允许我们从服务器控制页面上的元素。这非常强大。

HTMX In Drupal

HTMX支持使用 hx-*data-hx-* 属性。Drupal选择使用 data-hx-* 属性作为默认值。如果你愿意,仍然可以使用 hx-* 属性,但Drupal在渲染HTMX时默认将输出 data-hx-* 样式。

Drupal Libraries

HTMX通过一个或两个Drupal库包含到页面中。这些库如下:

  • core/htmx - 核心HTMX库。
  • core/drupal.htmx - 核心HTMX库以及一些用于与Drupal集成的额外脚本。

需要注意的是,你很少需要直接注入这些库。当你在Drupal中与HTMX交互时,它会自动将这些库注入到页面请求中。

core/drupal.htmx 库包含三个文件,在使用HTMX时协助Drupal。

  • htmx-assets.js - 它会查看Drupal的响应,并从接收到的标记中提取任何CSS和JavaScript引用。然后将这些资源注入到当前页面的头部,这意味着任何CSS或JavaScript都将成为当前页面的一部分,并用于渲染响应。这非常强大,因为这意味着我们可以在后端渲染任何我们需要的内容,然后确保它们在前端正确显示。
  • htmx-behaviors.js - 它连接到 Drupal.behaviors 系统,以便HTMX所做的任何DOM更改都可以在Drupal行为中注册并用于运行事件。通过添加这个文件,我们可以确保HTMX元素与Drupal页面上现有的组件一起工作。
  • htmx-utils.js - 这个文件包含一些供其他两个文件使用的辅助函数。

为了配合这些库,我们在后端还有一些PHP类。

Drupal Classes

\Drupal\Core\Htmx 类用作HTMX库和渲染数组属性注入的包装器。它还可以注入用于响应HTMX的头部信息。要使用它,你只需要创建一个新的 Htmx 对象,告诉它你希望元素具有哪些HTMX属性,然后将其应用到渲染元素上。

例如,这里有一个简单的例子,我们获取一个渲染数组元素,并将 data-hx-swapdata-hx-trigger 属性应用到该元素上。

  
    $output['element'] = [
      '#type' => 'html_tag',
      '#tag' => 'p',
      '#value' => 'Content'
    ];

    $htmx = new Htmx();
    $htmx->get()
      ->swap('afterend')
      ->trigger('revealed')
      ->applyTo($output['element']);
  

渲染时,这将生成以下HTML。

  
    <p data-hx-get="/route" data-hx-swap="afterend" data-hx-trigger="revealed">Content</p>
  

你可以看到,这些方法映射到HTMX库中对应的属性,因此 trigger() 方法将创建一个 data-hx-trigger 属性,依此类推。

我们调用 get() 方法来设置一个GET请求。这个方法可以接受一个参数,即它应该用于执行GET请求的路由。如果我们省略这个参数(就像这里一样),则假设使用当前路由。

通过使用这种机制,我们会自动在页面附件中包含 core/drupal.htmx 库。这意味着如果你在页面中添加一个HTMX元素,那么你将自动获得在前端正确渲染和使用该元素所需的一切。

你可能还会看到如下语法,这本质上是一样的。这使用了一种称为 "实例化时的类成员访问" 的语法,自PHP 5.4版本以来就已经存在,但并不常见。

  
    (new Htmx())
      ->get()
      ->swap('afterend')
      ->trigger('revealed')
      ->applyTo($output['element']);
  

除了Htmx类,我们还有一个 \Drupal\Core\Htmx\HtmxRequestInfoTrait 特性,用于检测HTMX请求和相关头部信息。这个特性需要访问 request_stack 服务和一个名为 getRequest() 的方法,以便从当前请求中提取信息。

我在这里没有详细介绍,但HTMX会发出许多不同的头部信息,你可以在代码中监听这些信息,以获取有关请求的更多信息。这可以告诉你诸如哪个元素触发了请求之类的事情。

HTMX Drupal Response

Drupal能够使用包含你创建的标记的小HTML文档来响应HTMX响应,这使用了 \Drupal\Core\Render\MainContent\HtmxRenderer 类。

可以 直接使用 HtmxRenderer 服务进行响应,但通常通过几种不同的机制调用它会更容易。这可以是将查询参数 _wrapper_format=drupal_htmx 添加到被调用的URL中,或者使用一个在路由参数中添加了 _htmx_route=true 选项的路由。

要将 _wrapper_format 添加到路由中,你可以在设置请求时这样做。

  
    $htmx->get(Url::fromRoute(route_name: 'my_htmx_route', options: [
      'query' => [
        '_wrapper_format' => 'drupal_htmx',
      ],
    ]));
  

在响应请求的路由上设置 _htmx_route=true

  
    mymodule_controller_action_htmx:

联系我们

提供基于Drupal的门户网站、电子商务网站、移动应用开发及托管服务

长按加微信
长风云微信
长按关注公众号
长风云公众号