{"id":4338,"date":"2024-11-07T15:09:41","date_gmt":"2024-11-07T20:09:41","guid":{"rendered":"https:\/\/frontendmasters.com\/blog\/?p=4338"},"modified":"2024-11-14T11:07:18","modified_gmt":"2024-11-14T16:07:18","slug":"why-alpine-is-the-new-jquery-and-why-that-is-an-awesome-thing","status":"publish","type":"post","link":"https:\/\/frontendmasters.com\/blog\/why-alpine-is-the-new-jquery-and-why-that-is-an-awesome-thing\/","title":{"rendered":"Why Alpine is the new jQuery and Why that is an Awesome Thing"},"content":{"rendered":"\n<p>Back in the old days, when I was building web sites by hand, in the snow, uphill, both ways,&nbsp;<a href=\"https:\/\/jquery.com\/\">jQuery<\/a>&nbsp;was my default tool when building any kind of interactivity on a web page. Way before I even considered building apps, jQuery was the workhorse that made cross-browser web development easy, or at least a little less painful. In 2024, it&#8217;s still in use on the vast majority of web sites. (I&#8217;ve seen various numbers, but all point to&nbsp;<em>at least<\/em>&nbsp;roughly three fourths of the web sites in use today.)<\/p>\n\n\n\n<p>I think part of the reason jQuery was so successful is that, along with patching browser incompatibilities (looking at you,&nbsp;Safari &amp; Internet Explorer), it was a laser focused set of utilities for common things developers needed to do. Mainly:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Making network requests (without the pain of <code>XMLHttpRequest<\/code>)<\/li>\n\n\n\n<li>Listening for events in the DOM<\/li>\n\n\n\n<li>Making changes in the DOM<\/li>\n<\/ul>\n\n\n\n<p>It did a lot more than that, but those three items are part of every interactive web page I&#8217;ve built since the introduction of JavaScript. This is why I&#8217;ve been so enamored of late with&nbsp;<a href=\"https:\/\/alpinejs.dev\/\">Alpine.js<\/a>. Alpine.js is lightweight (44kb minified, around half of jQuery&#8217;s size) and simple enough that the entire thing (at a high level), is documented <em>on the home page<\/em>. Let&#8217;s quickly go over the basics, and hopefully you&#8217;ll fall in love as well!<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"installation-and-setup\">Installation and Setup<\/h2>\n\n\n\n<p>You can do a&nbsp;<code>npm install alpinejs<\/code>&nbsp;if you want, but it&#8217;s easier to drop the CDN link in the head of your web page:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-1\" data-shcb-language-name=\"HTML, XML\" data-shcb-language-slug=\"xml\"><span><code class=\"hljs language-xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">script<\/span> <span class=\"hljs-attr\">defer<\/span> <span class=\"hljs-attr\">src<\/span>=<span class=\"hljs-string\">\"https:\/\/cdn.jsdelivr.net\/npm\/alpinejs@3.x.x\/dist\/cdn.min.js\"<\/span>&gt;<\/span><span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">script<\/span>&gt;<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-1\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">HTML, XML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">xml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>The next part requires a bit of thought. jQuery, once installed, was available everywhere and you could do just about anything. If you ever used Vue.js for progressive enhancement (versus as your entire application), you might remember that you had to specify the part of the DOM that Vue would work with. Basically, &#8220;This area of the DOM will make use of variables, have event handlers I track and so forth.&#8221; This usually was the main block of your web page, not the header and footer.<\/p>\n\n\n\n<p>Alpine is the same way. To keep things simple, I&#8217;ll be using one main&nbsp;<code>&lt;div&gt;<\/code>&nbsp;block for this purpose. This is done with an&nbsp;<code>x-data<\/code>&nbsp;attribute:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-2\" data-shcb-language-name=\"HTML, XML\" data-shcb-language-slug=\"xml\"><span><code class=\"hljs language-xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">x-data<\/span>=<span class=\"hljs-string\">\"stuff here in a bit...\"<\/span>&gt;<\/span>\n<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-2\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">HTML, XML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">xml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>OK, ready to get started?<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"setting-and-displaying-variables\">Setting and Displaying Variables<\/h2>\n\n\n\n<p>Let&#8217;s begin by initializing a few variables and showing how Alpine renders them. First, to define variables, you can set them as an object inside&nbsp;<code>x-data<\/code>:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-3\" data-shcb-language-name=\"HTML, XML\" data-shcb-language-slug=\"xml\"><span><code class=\"hljs language-xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">x-data<\/span>=<span class=\"hljs-string\">\"{\n  name:'Raymond',\n  age:51,\n  cool:false\n}\"<\/span>&gt;<\/span>\n<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-3\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">HTML, XML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">xml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>To display these values, you can use two different directives.&nbsp;The <code>x-text<\/code>&nbsp;attribute will bind the text value of a variable to the DOM while&nbsp;<code>x-html<\/code>&nbsp;will bind HTML. Which you use depends on the data. Here&#8217;s this in action:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-4\" data-shcb-language-name=\"HTML, XML\" data-shcb-language-slug=\"xml\"><span><code class=\"hljs language-xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">x-data<\/span>=<span class=\"hljs-string\">\"{\n  name:'Raymond',\n  age:51,\n  cool:false\n}\"<\/span>&gt;<\/span>\n  <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">p<\/span>&gt;<\/span>\n    My name is <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">span<\/span> <span class=\"hljs-attr\">x-text<\/span>=<span class=\"hljs-string\">\"name\"<\/span>&gt;<\/span><span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">span<\/span>&gt;<\/span>.\n    I'm <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">span<\/span> <span class=\"hljs-attr\">x-text<\/span>=<span class=\"hljs-string\">\"age\"<\/span>&gt;<\/span><span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">span<\/span>&gt;<\/span> years old.\n  <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">p<\/span>&gt;<\/span>\n<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-4\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">HTML, XML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">xml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Unlike Vue, Alpine.js doesn&#8217;t have a &#8220;mustache-like&#8221; language built in, but instead relies on attributes applied to your DOM itself. In this case,&nbsp;<code>span<\/code>&nbsp;tags. You could bind them to any element, but&nbsp;<code>span<\/code>s make sense here for simple values. I will admit, it&nbsp;<em>is<\/em>&nbsp;a bit verbose, but it&#8217;s nice that it&#8217;s easy to spot in code. You can see this in action below (and feel free to edit the values of course):<\/p>\n\n\n\n<div class=\"wp-block-cp-codepen-gutenberg-embed-block cp_embed_wrapper\"><iframe id=\"cp_embed_VwoQNXK\" src=\"\/\/codepen.io\/anon\/embed\/VwoQNXK?height=450&amp;theme-id=47434&amp;slug-hash=VwoQNXK&amp;default-tab=html,result&amp;editable=true\" height=\"450\" scrolling=\"no\" frameborder=\"0\" allowfullscreen allowpaymentrequest name=\"CodePen Embed VwoQNXK\" title=\"CodePen Embed VwoQNXK\" class=\"cp_embed_iframe\" style=\"width:100%;overflow:hidden\">CodePen Embed Fallback<\/iframe><\/div>\n\n\n\n<p>That&#8217;s basic &#8220;render data from JavaScript in the DOM&#8221;, but let&#8217;s now show how to&nbsp;<em>conditionally<\/em>&nbsp;show information. Like Vue, Alpine provides two methods. The first,&nbsp;<code>x-show<\/code>, will hide or display items in the DOM based on their value:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-5\" data-shcb-language-name=\"HTML, XML\" data-shcb-language-slug=\"xml\"><span><code class=\"hljs language-xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">x-data<\/span>=<span class=\"hljs-string\">\"{\n  name:'Raymond',\n  age:51,\n  cool:false\n}\"<\/span>&gt;<\/span>\n  <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">p<\/span>&gt;<\/span>\n    My name is <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">span<\/span> <span class=\"hljs-attr\">x-text<\/span>=<span class=\"hljs-string\">\"name\"<\/span>&gt;<\/span><span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">span<\/span>&gt;<\/span>.\n    I'm <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">span<\/span> <span class=\"hljs-attr\">x-text<\/span>=<span class=\"hljs-string\">\"age\"<\/span>&gt;<\/span><span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">span<\/span>&gt;<\/span> years old.\n  <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">p<\/span>&gt;<\/span>\n  <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">p<\/span> <span class=\"hljs-attr\">x-show<\/span>=<span class=\"hljs-string\">\"cool\"<\/span>&gt;<\/span>\n    Oh, and I'm so darn cool!\n  <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">p<\/span>&gt;<\/span>\n<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-5\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">HTML, XML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">xml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>The other method,&nbsp;<code>x-if<\/code>, will add and remove the contents of your DOM based on the value. Because of this, it requires you make use of&nbsp;<code>template<\/code>&nbsp;<strong>and<\/strong>&nbsp;use one top level root element, like a&nbsp;<code>div<\/code>&nbsp;or&nbsp;<code>p<\/code>. I&#8217;ll admit this tripped me up from time to time, but Alpine does a good job of reporting the issue if you screw this up. Here&#8217;s the same example as above, re-written with&nbsp;<code>x-if<\/code>:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-6\" data-shcb-language-name=\"HTML, XML\" data-shcb-language-slug=\"xml\"><span><code class=\"hljs language-xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">template<\/span> <span class=\"hljs-attr\">x-if<\/span>=<span class=\"hljs-string\">\"cool\"<\/span>&gt;<\/span>\n  <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">p<\/span>&gt;<\/span>\n    Did I mention I'm cool?\n  <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">p<\/span>&gt;<\/span>\n<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">template<\/span>&gt;<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-6\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">HTML, XML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">xml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>You can test this below. Switch the value of&nbsp;<code>cool<\/code>&nbsp;to see it in action:<\/p>\n\n\n\n<div class=\"wp-block-cp-codepen-gutenberg-embed-block cp_embed_wrapper\"><iframe id=\"cp_embed_XWvqbLN\" src=\"\/\/codepen.io\/anon\/embed\/XWvqbLN?height=450&amp;theme-id=47434&amp;slug-hash=XWvqbLN&amp;default-tab=html,result&amp;editable=true\" height=\"450\" scrolling=\"no\" frameborder=\"0\" allowfullscreen allowpaymentrequest name=\"CodePen Embed XWvqbLN\" title=\"CodePen Embed XWvqbLN\" class=\"cp_embed_iframe\" style=\"width:100%;overflow:hidden\">CodePen Embed Fallback<\/iframe><\/div>\n\n\n\n<p>Finally, what about looping? Alpine provides the&nbsp;<code>x-for<\/code>&nbsp;directive. Like&nbsp;<code>x-if<\/code>, you&#8217;ll use a&nbsp;<code>template<\/code>&nbsp;tag with one root element. Here&#8217;s an example:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-7\" data-shcb-language-name=\"HTML, XML\" data-shcb-language-slug=\"xml\"><span><code class=\"hljs language-xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">x-data<\/span>=<span class=\"hljs-string\">\"{\n  name:'Raymond',\n  age:51,\n  cool:false,\n  hobbies:&#91;'building cat demos','star wars','cats']\n}\"<\/span>&gt;<\/span>\n  <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">p<\/span>&gt;<\/span>\n    My name is <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">span<\/span> <span class=\"hljs-attr\">x-text<\/span>=<span class=\"hljs-string\">\"name\"<\/span>&gt;<\/span><span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">span<\/span>&gt;<\/span>.\n    I'm <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">span<\/span> <span class=\"hljs-attr\">x-text<\/span>=<span class=\"hljs-string\">\"age\"<\/span>&gt;<\/span><span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">span<\/span>&gt;<\/span> years old.\n  <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">p<\/span>&gt;<\/span>\n  <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">ul<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">template<\/span> <span class=\"hljs-attr\">x-for<\/span>=<span class=\"hljs-string\">\"hobby in hobbies\"<\/span>&gt;<\/span>\n      <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">li<\/span> <span class=\"hljs-attr\">x-text<\/span>=<span class=\"hljs-string\">\"hobby\"<\/span>&gt;<\/span><span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">li<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">template<\/span>&gt;<\/span>\n  <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">ul<\/span>&gt;<\/span>\n<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-7\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">HTML, XML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">xml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Note the use of &#8220;variable in array&#8221; syntax. You can use whatever you want here, but name it something sensible. Also, in the example above I&#8217;m looping over an array of strings. You can loop over an array of objects as well. Check it out in the embed below:<\/p>\n\n\n\n<div class=\"wp-block-cp-codepen-gutenberg-embed-block cp_embed_wrapper\"><iframe id=\"cp_embed_YzmLyzv\" src=\"\/\/codepen.io\/anon\/embed\/YzmLyzv?height=450&amp;theme-id=47434&amp;slug-hash=YzmLyzv&amp;default-tab=html,result\" height=\"450\" scrolling=\"no\" frameborder=\"0\" allowfullscreen allowpaymentrequest name=\"CodePen Embed YzmLyzv\" title=\"CodePen Embed YzmLyzv\" class=\"cp_embed_iframe\" style=\"width:100%;overflow:hidden\">CodePen Embed Fallback<\/iframe><\/div>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"two-way-binding\">Two-Way Binding<\/h2>\n\n\n\n<p>How about binding form fields to your data? Once again, Alpine provides a directive,&nbsp;<code>x-model<\/code>. This works on any form field&nbsp;<em>except<\/em>&nbsp;file inputs (although, like in Vue, there&#8217;s a workaround).<\/p>\n\n\n\n<p>For example:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-8\" data-shcb-language-name=\"HTML, XML\" data-shcb-language-slug=\"xml\"><span><code class=\"hljs language-xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">label<\/span> <span class=\"hljs-attr\">for<\/span>=<span class=\"hljs-string\">\"firstName\"<\/span>&gt;<\/span>First Name:<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">label<\/span>&gt;<\/span>\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">input<\/span> <span class=\"hljs-attr\">id<\/span>=<span class=\"hljs-string\">\"firstName\"<\/span> <span class=\"hljs-attr\">x-model<\/span>=<span class=\"hljs-string\">\"firstName\"<\/span>&gt;<\/span>\n\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">br<\/span> \/&gt;<\/span>\n\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">label<\/span> <span class=\"hljs-attr\">for<\/span>=<span class=\"hljs-string\">\"lastName\"<\/span>&gt;<\/span>Last Name:<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">label<\/span>&gt;<\/span>\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">input<\/span> <span class=\"hljs-attr\">id<\/span>=<span class=\"hljs-string\">\"lastName\"<\/span> <span class=\"hljs-attr\">x-model<\/span>=<span class=\"hljs-string\">\"lastName\"<\/span>&gt;<\/span><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">br<\/span>\/&gt;<\/span>\n\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">label<\/span> <span class=\"hljs-attr\">for<\/span>=<span class=\"hljs-string\">\"cool\"<\/span>&gt;<\/span>Cool?<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">label<\/span>&gt;<\/span>\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">select<\/span> <span class=\"hljs-attr\">id<\/span>=<span class=\"hljs-string\">\"cool\"<\/span> <span class=\"hljs-attr\">x-model<\/span>=<span class=\"hljs-string\">\"cool\"<\/span>&gt;<\/span>\n  <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">option<\/span>&gt;<\/span>true<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">option<\/span>&gt;<\/span>\n  <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">option<\/span>&gt;<\/span>false<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">option<\/span>&gt;<\/span>\n<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">select<\/span>&gt;<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-8\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">HTML, XML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">xml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>The embed below demonstrates this, along with conditionally showing content based on the dropdown:<\/p>\n\n\n\n<div class=\"wp-block-cp-codepen-gutenberg-embed-block cp_embed_wrapper\"><iframe id=\"cp_embed_MWNGabj\" src=\"\/\/codepen.io\/anon\/embed\/MWNGabj?height=450&amp;theme-id=47434&amp;slug-hash=MWNGabj&amp;default-tab=html,result\" height=\"450\" scrolling=\"no\" frameborder=\"0\" allowfullscreen allowpaymentrequest name=\"CodePen Embed MWNGabj\" title=\"CodePen Embed MWNGabj\" class=\"cp_embed_iframe\" style=\"width:100%;overflow:hidden\">CodePen Embed Fallback<\/iframe><\/div>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"binding-attributes\">Binding Attributes<\/h2>\n\n\n\n<p>Closely related to two-way binding of form fields is the simpler act of binding an HTML attribute to your data. Alpine, again, provides a directive for this,&nbsp;<code>x-bind<\/code>, as well a shortcut.<\/p>\n\n\n\n<p>Given your data has a value for&nbsp;<code>catPic<\/code>, we can bind it like so:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-9\" data-shcb-language-name=\"HTML, XML\" data-shcb-language-slug=\"xml\"><span><code class=\"hljs language-xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">img<\/span> <span class=\"hljs-attr\">x-bind:src<\/span>=<span class=\"hljs-string\">\"catPic\"<\/span>&gt;<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-9\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">HTML, XML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">xml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Because this is something folks may use quite a bit, Alpine provides a shortcut:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-10\" data-shcb-language-name=\"HTML, XML\" data-shcb-language-slug=\"xml\"><span><code class=\"hljs language-xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">img<\/span> <span class=\"hljs-attr\">:src<\/span>=<span class=\"hljs-string\">\"catPic2\"<\/span>&gt;<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-10\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">HTML, XML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">xml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>I feel like a live embed of this would be gratuitous given how simple this is, but as it&#8217;s pictures of cats, sorry, you&#8217;re getting an embed:<\/p>\n\n\n\n<div class=\"wp-block-cp-codepen-gutenberg-embed-block cp_embed_wrapper\"><iframe id=\"cp_embed_bGXMVad\" src=\"\/\/codepen.io\/anon\/embed\/bGXMVad?height=450&amp;theme-id=47434&amp;slug-hash=bGXMVad&amp;default-tab=html,result\" height=\"450\" scrolling=\"no\" frameborder=\"0\" allowfullscreen allowpaymentrequest name=\"CodePen Embed bGXMVad\" title=\"CodePen Embed bGXMVad\" class=\"cp_embed_iframe\" style=\"width:100%;overflow:hidden\">CodePen Embed Fallback<\/iframe><\/div>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"events\">Events<\/h2>\n\n\n\n<p>As you can probably guess by now, events are supported by a directive, this time the&nbsp;<code>x-on<\/code>&nbsp;directive where you specify the event and the handler to call. As with attribute bindings, there&#8217;s a shortcut. Here&#8217;s a simple example:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-11\" data-shcb-language-name=\"HTML, XML\" data-shcb-language-slug=\"xml\"><span><code class=\"hljs language-xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">x-data<\/span>=<span class=\"hljs-string\">\"{\n  catPic:'https:\/\/placecats.com\/400\/400',\n  flipImage() {\n    if (this.catPic.includes('\/g')) this.catPic = this.catPic.replace('\/g', '');\n    else this.catPic = this.catPic.replace('\/400', '\/g\/400');\n  }\t\t\t\t\t\t \n}\"<\/span>&gt;<\/span>\n  <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">img<\/span> <span class=\"hljs-attr\">:src<\/span>=<span class=\"hljs-string\">\"catPic\"<\/span> <span class=\"hljs-attr\">x-on:click<\/span>=<span class=\"hljs-string\">\"flipImage\"<\/span>&gt;<\/span>\n<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-11\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">HTML, XML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">xml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>I&#8217;ve defined a click handler on the image that runs a method,&nbsp;<code>flipImage<\/code>. If you look up in the&nbsp;<code>x-data<\/code>&nbsp;block, you can see I&#8217;ve defined the function there. In Alpine, your data can consist of various variables&nbsp;<em>and<\/em>&nbsp;methods interchangeably. Also note that&nbsp;<code>flipImage<\/code>&nbsp;uses the&nbsp;<code>this<\/code>&nbsp;scope to refer to the variable,&nbsp;<code>catPic<\/code>. All in all, this flips out a static picture of a cat with a grayscale version.<\/p>\n\n\n\n<p>The shorthand removes&nbsp;<code>x-on:<\/code>&nbsp;and simply uses&nbsp;<code>@<\/code>:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-12\" data-shcb-language-name=\"HTML, XML\" data-shcb-language-slug=\"xml\"><span><code class=\"hljs language-xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">img<\/span> <span class=\"hljs-attr\">:src<\/span>=<span class=\"hljs-string\">\"catPic\"<\/span> @<span class=\"hljs-attr\">click<\/span>=<span class=\"hljs-string\">\"flipImage\"<\/span>&gt;<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-12\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">HTML, XML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">xml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>You can play with this below:<\/p>\n\n\n\n<div class=\"wp-block-cp-codepen-gutenberg-embed-block cp_embed_wrapper\"><iframe id=\"cp_embed_mdNLVaR\" src=\"\/\/codepen.io\/anon\/embed\/mdNLVaR?height=450&amp;theme-id=47434&amp;slug-hash=mdNLVaR&amp;default-tab=html,result&amp;editable=true\" height=\"450\" scrolling=\"no\" frameborder=\"0\" allowfullscreen allowpaymentrequest name=\"CodePen Embed mdNLVaR\" title=\"CodePen Embed mdNLVaR\" class=\"cp_embed_iframe\" style=\"width:100%;overflow:hidden\">CodePen Embed Fallback<\/iframe><\/div>\n\n\n\n<p>Alpine also supports various modifies for event handling including the ability to run events once, prevent default behavior, throttle, and more. Check the&nbsp;<a href=\"https:\/\/alpinejs.dev\/directives\/on#modifiers\">modifiers<\/a>&nbsp;docs for more examples.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"lets-discuss-the-smell\">Let&#8217;s Discuss the Smell&#8230;<\/h2>\n\n\n\n<p>I can still remember the first presentation I sat in discussing Alpine. I remember thinking: this looks really simple and practical, but there&#8217;s no way in heck I&#8217;m going to write a bunch of JavaScript code all inside an HTML attribute on a <code>div<\/code>. Surely I thought,&nbsp;<em>surely<\/em>, the library&#8217;s not going to require me to do that?<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full is-resized\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"650\" height=\"484\" src=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2024\/11\/surely.jpg?resize=650%2C484&#038;ssl=1\" alt=\"\" class=\"wp-image-4341\" style=\"width:310px;height:auto\" srcset=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2024\/11\/surely.jpg?w=650&amp;ssl=1 650w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2024\/11\/surely.jpg?resize=300%2C223&amp;ssl=1 300w\" sizes=\"auto, (max-width: 650px) 100vw, 650px\" \/><\/figure>\n<\/div>\n\n\n<p>Of course there is! To begin, you switch the&nbsp;<code>x-data<\/code>&nbsp;directive from a block of variables and code to simply a name. That name can be anything, but I usually go with&nbsp;<code>app<\/code>. If for some reason I had multiple unrelated Alpine blocks of code on one page I&#8217;d use something more descriptive, but&nbsp;<code>app<\/code>&nbsp;is good enough:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-13\" data-shcb-language-name=\"HTML, XML\" data-shcb-language-slug=\"xml\"><span><code class=\"hljs language-xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">x-data<\/span>=<span class=\"hljs-string\">\"app\"<\/span>&gt;<\/span>\n  <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">img<\/span> <span class=\"hljs-attr\">:src<\/span>=<span class=\"hljs-string\">\"catPic\"<\/span> @<span class=\"hljs-attr\">click<\/span>=<span class=\"hljs-string\">\"flipImage\"<\/span>&gt;<\/span>\n<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-13\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">HTML, XML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">xml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>In your JavaScript, you first listen for the&nbsp;<code>alpine:init<\/code>&nbsp;event. This is an event thrown when Alpine itself is loaded, before it tries to interact with the page:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-14\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\"><span class=\"hljs-built_in\">document<\/span>.addEventListener(<span class=\"hljs-string\">\"alpine:init\"<\/span>, () =&gt; {\n  <span class=\"hljs-comment\">\/\/ stuff here...<\/span>\n});<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-14\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Then you can use&nbsp;<code>Alpine.data<\/code>&nbsp;to initialize your application. Here&#8217;s the complete code block:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-15\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\"><span class=\"hljs-built_in\">document<\/span>.addEventListener(<span class=\"hljs-string\">\"alpine:init\"<\/span>, () =&gt; {\n  Alpine.data(<span class=\"hljs-string\">\"app\"<\/span>, () =&gt; ({\n    <span class=\"hljs-attr\">catPic<\/span>: <span class=\"hljs-string\">\"https:\/\/placecats.com\/400\/400\"<\/span>,\n    flipImage() {\n      <span class=\"hljs-keyword\">if<\/span> (<span class=\"hljs-keyword\">this<\/span>.catPic.includes(<span class=\"hljs-string\">\"\/g\"<\/span>))\n        <span class=\"hljs-keyword\">this<\/span>.catPic = <span class=\"hljs-keyword\">this<\/span>.catPic.replace(<span class=\"hljs-string\">\"\/g\"<\/span>, <span class=\"hljs-string\">\"\"<\/span>);\n      <span class=\"hljs-keyword\">else<\/span> <span class=\"hljs-keyword\">this<\/span>.catPic = <span class=\"hljs-keyword\">this<\/span>.catPic.replace(<span class=\"hljs-string\">\"\/400\"<\/span>, <span class=\"hljs-string\">\"\/g\/400\"<\/span>);\n    }\n  }));\n});<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-15\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>This is&nbsp;<em>much<\/em>&nbsp;cleaner and lets you keep your HTML and JavaScript separated as it should be. (IMO anyway!) You can see this version below:<\/p>\n\n\n\n<div class=\"wp-block-cp-codepen-gutenberg-embed-block cp_embed_wrapper\"><iframe id=\"cp_embed_qBeYqGX\" src=\"\/\/codepen.io\/anon\/embed\/qBeYqGX?height=450&amp;theme-id=47434&amp;slug-hash=qBeYqGX&amp;default-tab=html,result\" height=\"450\" scrolling=\"no\" frameborder=\"0\" allowfullscreen allowpaymentrequest name=\"CodePen Embed qBeYqGX\" title=\"CodePen Embed qBeYqGX\" class=\"cp_embed_iframe\" style=\"width:100%;overflow:hidden\">CodePen Embed Fallback<\/iframe><\/div>\n\n\n\n<p class=\"learn-more\">Note: Notice I&#8217;m including the Alpine <code>&lt;script&gt;<\/code> tag in the HTML instead of using CodePen&#8217;s JavaScript settings so you can clearly see it and so that I can add the <code>defer<\/code> attribute.<\/p>\n\n\n\n<p>With our logic now separated in code, it becomes easier to add new features. For example, by adding an&nbsp;<code>init<\/code>&nbsp;function, Alpine will automatically run the method when the application is loaded. In the&nbsp;<em>incredibly<\/em>&nbsp;simple application below, the&nbsp;<code>init<\/code>&nbsp;method is used to request a Dad Joke immediately:<\/p>\n\n\n\n<div class=\"wp-block-cp-codepen-gutenberg-embed-block cp_embed_wrapper\"><iframe id=\"cp_embed_qBeYymp\" src=\"\/\/codepen.io\/anon\/embed\/qBeYymp?height=550&amp;theme-id=47434&amp;slug-hash=qBeYymp&amp;default-tab=html,result\" height=\"550\" scrolling=\"no\" frameborder=\"0\" allowfullscreen allowpaymentrequest name=\"CodePen Embed qBeYymp\" title=\"CodePen Embed qBeYymp\" class=\"cp_embed_iframe\" style=\"width:100%;overflow:hidden\">CodePen Embed Fallback<\/iframe><\/div>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"when-not-to-use-alpine\">When <em>not<\/em> to use Alpine<\/h2>\n\n\n\n<p>I just spent the last two thousand or so words explaining the basics of Alpine and raving about how much I love it so it would be crazy for me to tell you&nbsp;<em>not<\/em>&nbsp;to use it, right? Years ago, when I was much younger and foolish, I&nbsp;<em>always<\/em>&nbsp;reached for a JavaScript framework. First Angular, than Vue. Now that I&#8217;m much more mature and rarely make mistakes (ahem), my default is vanilla JavaScript, and by that I mean, no framework. If I just need a few lines of code, it would be silly to load anything I don&#8217;t need, even Alpine. That being said, when I&#8217;m building something that is doing a lot of DOM manipulation, needs proper two-way binding, or just feels like, mentally, I need an &#8220;assistant&#8221;, Alpine is what I go to first.<\/p>\n\n\n\n<p>With that, let me leave you with not one, but two Alpine examples I&#8217;m particularly proud of. The first is&nbsp;<a href=\"https:\/\/idlefleet.netlify.app\/\">IdletFleet<\/a>, a simple &#8220;idle clicker&#8221; game where you work to build a space trading empire. Emphasis on the simple.<\/p>\n\n\n\n<p>Next is&nbsp;<a href=\"https:\/\/catherder.netlify.app\/\">Cat Herder<\/a>, another &#8220;idle clicker&#8221; game but since it involves cats, you can&#8217;t be quite as idle.<\/p>\n\n\n\n<p>Both games have links to their respective repositories where you can dig into the code and how Alpine helped, but I encourage you to play both a bit before you peek behind the curtains.<\/p>\n\n\n\n<p>Also be sure to&nbsp;<a href=\"https:\/\/alpinejs.dev\/\">peruse&nbsp;the Alpine docs<\/a> as I didn&#8217;t cover quite everything, but you can easily read the complete docs in less than an hour.<a href=\"https:\/\/github.com\/cfjedimaster\/writing\/blob\/main\/intro_to_alpine\/article.md#why-alpine-is-the-new-jquery-and-why-that-is-an-awesome-thing\"><\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>jQuery for was great and all, but Alpine.js simplifies tasks like data binding and DOM manipulation, providing using a more declarative and clear structure, making it ideal for smaller projects.<\/p>\n","protected":false},"author":29,"featured_media":4348,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"sig_custom_text":"","sig_image_type":"featured-image","sig_custom_image":0,"sig_is_disabled":false,"inline_featured_image":false,"_jetpack_memberships_contains_paid_content":false,"footnotes":""},"categories":[1],"tags":[257,3],"class_list":["post-4338","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-blog-post","tag-alpine","tag-javascript"],"acf":[],"jetpack_featured_media_url":"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2024\/11\/alpine.png?fit=1760%2C872&ssl=1","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/posts\/4338","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/users\/29"}],"replies":[{"embeddable":true,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/comments?post=4338"}],"version-history":[{"count":10,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/posts\/4338\/revisions"}],"predecessor-version":[{"id":4426,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/posts\/4338\/revisions\/4426"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/media\/4348"}],"wp:attachment":[{"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/media?parent=4338"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/categories?post=4338"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/tags?post=4338"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}