Preface
Goal: Moving Alpine.js’s attribute using
x-spread
.
Writing Alpine.js
is like writing configuration,
as opposed with writing logic in coding.
Article Steps
The alpine.js
is divided into two parts.
-
Inline Alpine.js, then
-
Extracted Alpine.js.
The first one has already discussed in previous article. Now all we need is, to extract from the element.
Examples
For tutorial purpose we need two layouts:
-
Simple layout, to explain jQuery.
-
Enhance layout, as real live example.
Preview
Simple Tabs : Structure
Document: HTML Head
We can reuse previous tailwind
stylesheet.
<title>Simple Tabs - Alpine.js (extracted)</title>
<link rel="stylesheet" type="text/css"
href="../css/03-tailwind-simple.css">
<script src="../js/vendors/alpine.min.js"></script>
<script src="../js/custom/alpine-simple.js"></script>
Yes, we are going to move stuff to an external javascript.
Document: HTML Body: Tab Headers
Now it is as simple as:
<main class="tabs" x-data="tabs()">
<div class="tab-headers">
<div x-spread="header_home">Home</div>
<div x-spread="header_team">Team</div>
<div x-spread="header_news">News</div>
<div x-spread="header_about">About</div>
</div>
…
</div>
…
</main>
It looks tidy right 🙂?
How Does It Works?
We are going to initialize stuff with x-data=tabs()
.
All the configuration, bundled in this tabs()
function.
function tabs() {
return {
tab: 'news',
// … more configuration here
}
}
Javascript: Handling Headers Event
The x-spread
manage all the attributes.
<div x-spread="header_home">Home</div>
So we can have the details somewhere else, such as below code.
function tabs() {
return {
tab: 'news',
header_home: {
['@click']() { this.tab = 'home' },
[':class']() {
return (this.tab == 'home') ? 'active bg-blue-500' : 'bg-gray-700';
}
},
// … more configuration here
}
}
The Issue with Alpine
We still have to write down the declaration, for each element such as below:
function tabs() {
return {
tab: 'news',
header_home: {
['@click']() { this.tab = 'home' },
[':class']() {
return (this.tab == 'home') ? 'active bg-blue-500' : 'bg-gray-700';
}
},
header_team: {
['@click']() { this.tab = 'team' },
[':class']() {
return (this.tab == 'team') ? 'active bg-teal-500' : 'bg-gray-700';
}
},
header_news: {
['@click']() { this.tab = 'news' },
[':class']() {
return (this.tab == 'news') ? 'active bg-red-500' : 'bg-gray-700';
}
},
header_about: {
['@click']() { this.tab = 'about' },
[':class']() {
return (this.tab == 'about') ? 'active bg-orange-500' : 'bg-gray-700';
}
},
// … more configuration here
}
}
Or maybe it is just me. I just still don’t know how to do it yet.
Document: HTML Body: Tab Content
The same writing style, also applied the tab contents.
<div class="tab-contents">
<div x-spread="content_home">
<h3>Home</h3>
<p>Lorem ipsum dolor sit amet,
consectetur adipiscing elit.
Quisque in faucibus magna.</p>
</div>
<div x-spread="content_team">
<h3>Team</h3>
<p>Nulla luctus nisl in venenatis vestibulum.
Nam consectetur blandit consectetur.</p>
</div>
<div x-spread="content_news">
<h3>News</h3>
<p>Phasellus vel tempus mauris,
quis mattis leo.
Mauris quis velit enim.</p>
</div>
<div x-spread="content_about">
<h3>About</h3>
<p>Interdum et malesuada fames ac ante
ipsum primis in faucibus.
Nulla vulputate tortor turpis,
at eleifend eros bibendum quis.</p>
</div>
</div>
Javascript: Handling Headers Event to Tab Content
The x-spread
manage all the attributes.
Consider this example below, for home
tab:
<main class="tabs" x-data="tabs()">
<div class="tab-headers">
<div x-spread="header_home">Home</div>
…
</div>
<div class="tab-spacer"></div>
<div class="tab-contents">
<div x-spread="content_home">
<h3>Home</h3>
<p>Lorem ipsum dolor sit amet,
consectetur adipiscing elit.
Quisque in faucibus magna.</p>
</div>
…
</div>
</main>
The same extracted attributes, also applied to the tab contents.
function tabs() {
return {
tab: 'news',
header_home: {
['@click']() { this.tab = 'home' },
[':class']() {
return (this.tab == 'home') ? 'active bg-blue-500' : 'bg-gray-700';
}
},
// … more configuration here
content_home: {
['x-show']() { return (this.tab == 'home'); },
[':class']() { return (this.tab == 'home') ? 'bg-blue-500' : ''; }
},
// … more configuration here
}
}
Again, I know it is long. But that is all.
Your code should be working by now.
Preview
The same as previous example.
The Riddle: Issue with Alpine!
Can we make this simpler?
Unfortunately I failed to combine the x-spread
with x-for
.
Perhaps I’m asking too much.
Or maybe I just do not know how to do it yet.
Enhanced Tabs: Structure
How about real live component? Let’s the fun begin.
Remember that we only add these two feature:
-
hover
and, -
bg-white
.
Document: HTML Head
We still can reuse previous stylesheet.
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Tabs - Alpine.js (extracted)</title>
<link rel="stylesheet" type="text/css" href="../css/04-tailwind-enhanced.css">
<link rel="stylesheet" type="text/css" href="../css/02-border-radius.css">
<script src="../js/vendors/alpine.min.js"></script>
<script src="../js/custom/alpine-enhanced.js"></script>
Document: HTML Body: Initialization
<main class="tabs" x-data="tabs()">
/* .. elements here … */
</main>
- .
How Does It Works?
The same as the simple example above.
But with hover
variable.
We are going to initialize stuff with x-data=tabs()
.
All the configuration, bundled in this tabs()
function.
function tabs() {
return {
tab: 'news',
hover: false,
// … more configuration here
}
}
Document: HTML Body: Tab Headers
We have the third level div
.
<div class="tab-headers">
<div class="tab-headers">
<div x-spread="header_home">
<div x-spread="header_inner_home">Home</div>
</div>
<div x-spread="header_team">
<div x-spread="header_inner_team">Team</div>
</div>
<div x-spread="header_news">
<div <div x-spread="header_inner_news">News</div>
</div>
<div x-spread="header_about">
<div <div x-spread="header_inner_about">About</div>
</div>
</div>
…
</div>
Now I feel good. The html
source code looks tidy.
Javascript: Handling Headers Event
The x-spread
manage all the attributes.
<div class="tab-contents">
<div x-spread="content_home">
<div class="tab-content" x-spread="content_inner_home">
<h3>Home</h3>
…
So we can have the details somewhere else, such as below code,
along with the x-on:mouseenter
and x-on:mouseleave
event.
function tabs() {
return {
tab: 'news',
hover: false,
header_home: {
['@click']() { this.tab = 'home'; this.hover = this.tab; },
['@mouseenter']() { this.hover = (this.tab == 'home') ? this.tab : false; },
['@mouseleave']() { this.hover = false },
[':class']() {
return (this.tab == 'home') ? 'active bg-blue-500' : 'bg-gray-700';
}
},
// … more configuration here
}
}
Document: HTML Body: Tab Content
We also have the third level div
here.
The same writing style, also applied the tab contents.
<div class="tab-contents">
<div x-spread="content_home">
<div class="tab-content" x-spread="content_inner_home">
<h3>Home</h3>
<p>Lorem ipsum dolor sit amet,
consectetur adipiscing elit.
Quisque in faucibus magna.</p>
</div>
</div>
…
</div>
Javascript: Handling Headers Event to Tab Content
The x-spread
manage all the attributes.
Consider this example below, for home
tab:
<main class="tabs" x-data="tabs()">
<div class="tab-headers">
<div x-spread="header_home">
<div x-spread="header_inner_home">Home</div>
</div>
…
</div>
<div class="tab-spacer"></div>
<div class="tab-contents">
<div x-spread="content_home">
<div class="tab-content" x-spread="content_inner_home">
<h3>Home</h3>
<p>Lorem ipsum dolor sit amet,
consectetur adipiscing elit.
Quisque in faucibus magna.</p>
</div>
</div>
</div>
</main>
The same extracted attributes, also applied to the tab contents.
function tabs() {
return {
tab: 'news',
hover: false,
header_home: {
['@click']() { this.tab = 'home'; this.hover = this.tab; },
['@mouseenter']() { this.hover = (this.tab == 'home') ? this.tab : false; },
['@mouseleave']() { this.hover = false },
[':class']() {
return (this.tab == 'home') ? 'active bg-blue-500' : 'bg-gray-700';
}
},
// … more configuration here
header_inner_home: {
[':class']() { return (this.tab == 'home') ? 'bg-white' : ''; }
},
// … more configuration here
content_home: {
['x-show']() { return (this.tab == 'home'); },
[':class']() { return (this.tab == 'home') ? 'bg-blue-500' : ''; }
},
// … more configuration here
content_inner_home: {
[':class']() { return (this.hover == 'home') ? 'is-hovered' : ''; }
},
// … more configuration here
}
}
You can examine yourself, how synching hover
implemented here:
function tabs() {
return {
tab: 'news',
hover: false,
header_home: {
['@click']() { this.tab = 'home'; this.hover = this.tab; },
['@mouseenter']() { this.hover = (this.tab == 'home') ? this.tab : false; },
['@mouseleave']() { this.hover = false }
}
},
// … more configuration here
content_inner_home: {
[':class']() { return (this.hover == 'home') ? 'is-hovered' : ''; }
},
// … more configuration here
}
}
I think that’s all.
Preview
The same as previous example.
Complete
If you want, I also provide a complete version with FontAwesome
.
But, this won’t be my final journey. I still want to explore modern javascript framework as well.
What’s Next?
This lightweight alpine.js
can be leveraged to vue.js
.
Consider continue reading [ Tabs - JS - Vue.js (component) ].