Skip to content

Commit 8264655

Browse files
author
Ajay kumar
committed
[Blog] [Ajay]: Add blog for custom elements
1 parent bd7c42a commit 8264655

File tree

10 files changed

+289
-91
lines changed

10 files changed

+289
-91
lines changed

TestArena/Blog/Common/CodeSnippet.razor

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,22 +3,26 @@
33
<div class="container">
44
<div class="row">
55
<div class="col-12">
6-
<pre class="pre-scrollable rounded border border-2 p-3">
6+
<pre class="pre-scrollable rounded border border-2 p-1 px-4">
77
<code class="language-@Language" style="font-size: 0.8rem;">@ChildContent</code>
88
</pre>
99
</div>
10-
<div class="col-12 text-center">
11-
<code>Code Sample #@(Number > 0 ? Number.ToString() : string.Empty) : @Description</code>
12-
</div>
10+
@if (ShowDescription)
11+
{
12+
<div class="col-12 text-center">
13+
<code>@(Number > 0 ? $"Code Sample #{Number.ToString()} : " : string.Empty)@Description</code>
14+
</div>
15+
}
1316
</div>
1417
</div>
1518
<br/>
1619

1720
@code {
1821
[Parameter] public RenderFragment? ChildContent { get; set; }
1922
[Parameter] public string Language { get; set; } = "csharp"; // Default to C#
20-
[Parameter] public int Number { get; set; } = 1; // Default to C#
23+
[Parameter] public int Number { get; set; } = 0; // Default to C#
2124
[Parameter] public string Description { get; set; } = ""; // Default to C#
25+
[Parameter] public bool ShowDescription { get; set; } = true; // Default to C#
2226
2327
protected override async Task OnAfterRenderAsync(bool firstRender)
2428
{

TestArena/Blog/Common/NavigationUtils/SiteMap.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,11 @@ public static class SiteMap
5353
new DateTime(2025, 3, 22),
5454
"images/blog/vulnerability/auditing/banner.png",
5555
["Security", "Vulnerability", ".NET"]),
56+
new("Custom Elements in HTML",
57+
"/blog/custom-elements-in-html",
58+
new DateTime(2025, 4, 5),
59+
"images/blog/html-blogs/custom-element/banner.png",
60+
["HTML", "Web Components", "Custom Elements"]),
5661
];
5762
}
5863

Lines changed: 270 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,270 @@
1+
@page "/blog/custom-elements-in-html"
2+
@using TestArena.Blog.Common
3+
@using TestArena.Blog.Common.NavigationUtils
4+
5+
<style>
6+
table {
7+
border-collapse: collapse;
8+
width: 100%;
9+
margin: 20px 0;
10+
}
11+
th, td {
12+
padding: 12px;
13+
border: 1px solid #ddd;
14+
text-align: left;
15+
}
16+
th {
17+
background-color: #f0f0f0;
18+
}
19+
.tip {
20+
background: #eaf4ff;
21+
padding: 10px;
22+
border-left: 5px solid #3498db;
23+
margin: 1em 0;
24+
}
25+
</style>
26+
@code{
27+
PageInfo currentPage = SiteMap.Pages.FirstOrDefault(x => x.RelativePath == "/blog/custom-elements-in-html");
28+
}
29+
30+
<BlogContainer>
31+
<Header Title="@currentPage.Header"
32+
Image="@currentPage.ArticleImage" PublishedOn="@currentPage.PublishedOn" Authors="Ajay Kumar">
33+
</Header>
34+
35+
<h2>🧩 Custom Elements in HTML</h2>
36+
37+
<p>HTML has evolved from a static markup language to a full-fledged platform for building web applications. One of its most powerful features is the ability to define <strong>Custom Elements</strong> — your very own HTML tags.</p>
38+
39+
<h3>✅ What Are Custom Elements?</h3>
40+
41+
<p><strong>Custom Elements</strong> are a key feature of the <strong>Web Components</strong> standard — a set of APIs that lets developers create reusable, encapsulated HTML elements with custom behavior.</p>
42+
43+
<div class="tip">
44+
🧠 <strong>Web Components</strong> are a suite of technologies (Custom Elements, Shadow DOM, HTML Templates) for building reusable UI components natively in the browser.
45+
</div>
46+
47+
<p>For example:</p>
48+
49+
<CodeSnippet Language="html" ShowDescription="false">
50+
&lt;user-profile name="Alice" age="30"&gt;&lt;/user-profile&gt;
51+
</CodeSnippet>
52+
@* <pre><code>&lt;user-profile name="Alice" age="30"&gt;&lt;/user-profile&gt;</code></pre> *@
53+
54+
<p>This <code>&lt;user-profile&gt;</code> tag is not part of standard HTML. It's a <strong>custom element</strong> you can define with specific behavior and UI.</p>
55+
56+
<p>Each custom element typically includes:</p>
57+
<ul>
58+
<li>A <strong>JavaScript class</strong> that defines its logic and rendering.</li>
59+
<li>A <strong>unique tag name</strong> (must include a hyphen).</li>
60+
<li>Optional <strong>lifecycle callbacks</strong> for reacting to events like insertion into the DOM or attribute changes.</li>
61+
</ul>
62+
63+
<h3>💡 Why Use Custom Elements?</h3>
64+
65+
<h4>1. Reusability</h4>
66+
<p>Encapsulate UI logic and use your element across multiple pages or projects — just like built-in tags.</p>
67+
68+
<h4>2. Encapsulation</h4>
69+
<p>By using the <strong>Shadow DOM</strong>, your element’s internal styles and structure are protected from outside interference.</p>
70+
71+
<div class="tip">
72+
🧠 <strong>Shadow DOM</strong> is a hidden DOM tree inside an element that isolates markup and styles from the main document, preventing clashes.
73+
</div>
74+
75+
<h4>3. Zero Dependencies</h4>
76+
<p>No need for a JavaScript framework — Custom Elements are built into the browser.</p>
77+
78+
<h4>4. Modular Architecture</h4>
79+
<p>Just like components in frameworks (React, Vue), Custom Elements promote maintainability and separation of concerns.</p>
80+
81+
<h3>📅 When Should You Use Custom Elements?</h3>
82+
83+
<p>Use custom elements when:</p>
84+
<ul>
85+
<li>You need reusable UI pieces that can live independently.</li>
86+
<li>You’re building framework-agnostic components for multiple teams.</li>
87+
<li>You want to encapsulate logic and styles tightly.</li>
88+
<li>You’re building a design system or web component library.</li>
89+
</ul>
90+
91+
<p><strong>Avoid</strong> using them if your project is already heavily coupled to a frontend framework — unless interoperability is a goal.</p>
92+
93+
<h3>🛠️ How to Create Custom Elements (Step-by-Step)</h3>
94+
95+
<p>Let’s build a simple, interactive custom element: <code>&lt;user-profile&gt;</code>.</p>
96+
97+
<h4>🔹 Step 1: Define the Element Class</h4>
98+
99+
<CodeSnippet Number="1" Language="javascript" Description="Defining the Element Class">
100+
class UserProfile extends HTMLElement {
101+
constructor() {
102+
super();
103+
this.attachShadow({ mode: 'open' });
104+
this.render();
105+
}
106+
107+
static get observedAttributes() {
108+
return ['name', 'age'];
109+
}
110+
111+
attributeChangedCallback(name, oldValue, newValue) {
112+
this.render();
113+
}
114+
115+
render() {
116+
const name = this.getAttribute('name') || 'Unknown';
117+
const age = this.getAttribute('age') || 'N/A';
118+
119+
this.shadowRoot.innerHTML = `
120+
&lt;style&gt;
121+
.profile {
122+
font-family: sans-serif;
123+
padding: 1em;
124+
border: 1px solid #ccc;
125+
border-radius: 8px;
126+
background: #f9f9f9;
127+
}
128+
&lt;/style&gt;
129+
&lt;div class="profile"&gt;
130+
&lt;h2&gt;${name}&lt;/h2&gt;
131+
&lt;p&gt;Age: ${age}&lt;/p&gt;
132+
&lt;/div&gt;
133+
`;
134+
}
135+
}
136+
</CodeSnippet>
137+
138+
<div class="tip">
139+
🧠 <strong>Lifecycle Callbacks</strong> like <code>attributeChangedCallback</code> fire at different stages of the element's life (e.g., insertion or attribute change).
140+
</div>
141+
142+
<h4>🔹 Step 2: Register the Custom Element</h4>
143+
144+
<CodeSnippet Number="2" Language="javascript" Description="Registering the Custom Element">
145+
customElements.define('user-profile', UserProfile);
146+
</CodeSnippet>
147+
148+
<div class="tip">
149+
🧠 Custom element tag names <strong>must include a hyphen</strong> to differentiate them from standard HTML tags.
150+
</div>
151+
152+
<h4>🔹 Step 3: Use It in HTML</h4>
153+
154+
<CodeSnippet Number="3" Language="html" Description="Using it in HTML">
155+
&lt;user-profile name="Jane Doe" age="28"&gt;&lt;/user-profile&gt;
156+
157+
&lt;script src="user-profile.js"&gt;&lt;/script&gt;
158+
</CodeSnippet>
159+
160+
<BlogImage Number="1" Description="Custom Element in Action"
161+
ImagePath="/images/blog/html-blogs/custom-element/Custom Element in Action.png" />
162+
163+
<h3>🎯 Bonus: Make It Interactive</h3>
164+
165+
<p>Let’s enhance it with a birthday button to increment the age.</p>
166+
167+
<CodeSnippet Number="4" Language="javascript" Description="Making It Interactive">
168+
render() {
169+
const name = this.getAttribute('name') || 'Unknown';
170+
const age = this.getAttribute('age') || 'N/A';
171+
172+
this.shadowRoot.innerHTML = `
173+
&lt;style&gt;
174+
.profile { font-family: sans-serif; }
175+
button { margin-top: 10px; }
176+
&lt;/style&gt;
177+
&lt;div class="profile"&gt;
178+
&lt;h2&gt;${name}&lt;/h2&gt;
179+
&lt;p&gt;Age: &lt;span id="age"&gt;${age}&lt;/span&gt;&lt;/p&gt;
180+
&lt;button id="birthdayBtn"&gt;Celebrate Birthday 🎂&lt;/button&gt;
181+
&lt;/div&gt;
182+
`;
183+
184+
this.shadowRoot.querySelector('#birthdayBtn').onclick = () => {
185+
let newAge = parseInt(this.getAttribute('age') || '0') + 1;
186+
this.setAttribute('age', newAge.toString());
187+
};
188+
}
189+
</CodeSnippet>
190+
191+
<BlogImage Number="2" Description="Interactive custom element in action"
192+
ImagePath="/images/blog/html-blogs/custom-element/Interactive custom element in action.gif" />
193+
194+
<h3>⚠️ Gotchas to Keep in Mind</h3>
195+
196+
<table>
197+
<thead>
198+
<tr>
199+
<th>Gotcha</th>
200+
<th>Why It Matters</th>
201+
<th>What You Should Do</th>
202+
</tr>
203+
</thead>
204+
<tbody>
205+
<tr>
206+
<td><strong>Tag name must include a hyphen</strong></td>
207+
<td>Avoids conflicts with native HTML tags</td>
208+
<td>Always use names like <code>my-button</code>, <code>user-profile</code></td>
209+
</tr>
210+
<tr>
211+
<td><strong>Shadow DOM may block external CSS</strong></td>
212+
<td>Styles in global CSS files won’t reach inside</td>
213+
<td>Define styles inside the shadow DOM or use CSS variables</td>
214+
</tr>
215+
<tr>
216+
<td><strong>Not supported in older browsers (e.g., IE11)</strong></td>
217+
<td>Your users might see broken UI</td>
218+
<td>Use polyfills or feature detection if backward compatibility is required</td>
219+
</tr>
220+
<tr>
221+
<td><strong>No data binding or templating out of the box</strong></td>
222+
<td>You may miss convenience features from frameworks</td>
223+
<td>Consider adding a light templating engine or integrate with frameworks carefully</td>
224+
</tr>
225+
<tr>
226+
<td><strong>Observed attributes must be explicitly listed</strong></td>
227+
<td>Your component won't react to attribute changes otherwise</td>
228+
<td>Always declare <code>observedAttributes</code> properly to enable dynamic updates</td>
229+
</tr>
230+
</tbody>
231+
</table>
232+
233+
<h3>📦 Packaging Tips</h3>
234+
<ul>
235+
<li>Bundle your custom elements using <strong>Vite</strong>, <strong>Rollup</strong>, or <strong>Webpack</strong> for modular usage.</li>
236+
<li>You can publish them to <strong>npm</strong> or host via <strong>CDN</strong> for global use.</li>
237+
<li>Prefix your components (<code>my-</code>, <code>app-</code>, etc.) to avoid name collisions.</li>
238+
</ul>
239+
240+
<h3>📚 Summary</h3>
241+
242+
<table>
243+
<tr>
244+
<th>Concept</th>
245+
<th>Description</th>
246+
</tr>
247+
<tr>
248+
<td><strong>What</strong></td>
249+
<td>Custom HTML tags with built-in behavior</td>
250+
</tr>
251+
<tr>
252+
<td><strong>Why</strong></td>
253+
<td>Reusable, encapsulated, framework-free components</td>
254+
</tr>
255+
<tr>
256+
<td><strong>When</strong></td>
257+
<td>For UI modules that are reused or shared</td>
258+
</tr>
259+
<tr>
260+
<td><strong>How</strong></td>
261+
<td>Define a class, use <code>customElements.define</code>, and drop in your HTML</td>
262+
</tr>
263+
</table>
264+
265+
<h3>🚀 Final Thoughts</h3>
266+
267+
<p>Custom Elements are a native way to bring component-based architecture to your frontend without the weight of a framework. They offer reusability, encapsulation, and portability across projects.</p>
268+
269+
<p>They’re a perfect fit when building reusable UI pieces, design systems, or standalone widgets. Try one today — you’ll appreciate how clean and maintainable your frontend can be.</p>
270+
</BlogContainer>

TestArena/Pages/Counter.razor

Lines changed: 0 additions & 19 deletions
This file was deleted.

TestArena/Pages/Home.razor

Lines changed: 0 additions & 7 deletions
This file was deleted.

0 commit comments

Comments
 (0)