feat: enhance blog post view counter with individual tracking and display
This commit is contained in:
13
src/blog.njk
13
src/blog.njk
@@ -9,15 +9,20 @@ eleventyExcludeFromCollections: true
|
||||
<div class="space-y-8">
|
||||
{%- for post in collections.general | reverse -%}
|
||||
<div class="card">
|
||||
<h2 class="text-2xl font-bold">
|
||||
<h2 class="text-2xl font-bold mb-1">
|
||||
<a href="{{ post.url }}" class="text-blue-400 hover:underline">{{ post.data.title }}</a>
|
||||
</h2>
|
||||
<p class="text-gray-400 mb-4">
|
||||
{{ post.date | readableDate }}
|
||||
</p>
|
||||
<div class="flex items-center text-gray-400 mb-4">
|
||||
<i class="fas fa-calendar mr-2"></i>
|
||||
<p>{{ post.date | readableDate }}</p>
|
||||
<span class="mx-2 text-gray-600">|</span>
|
||||
<i class="fas fa-eye mr-2"></i>
|
||||
<span class="view-count" data-view-count data-slug="{{ post.fileSlug }}">...</span>
|
||||
</div>
|
||||
{% if post.data.excerpt %}
|
||||
<p class="text-gray-300">{{ post.data.excerpt }}</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
{%- endfor -%}
|
||||
</div>
|
||||
<script src="/js/view-counter.js"></script>
|
||||
|
@@ -3,8 +3,8 @@ title: "Junos ZTP with Dnsmasq DHCP in OPNsense"
|
||||
date: "2025-08-14"
|
||||
layout: "post.njk"
|
||||
tags: "general"
|
||||
description: "Stop configuring your new lab devices manually!"
|
||||
excerpt: "Stop configuring your new lab devices manually! This guide will show you how to 'Zero Touch' provision Junos devices!"
|
||||
description: "Stop configuring your new lab devices manually! This guide will show you how to 'Zero Touch' provision Junos devices!"
|
||||
excerpt: "Stop configuring your new lab devices manually!"
|
||||
sitemapPriority: 0.8
|
||||
sitemapChangefreq: "weekly"
|
||||
image: "/assets/images/8.14.25/junos+opnsense.jpg"
|
||||
|
@@ -1,37 +1,70 @@
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
const viewCountElement = document.querySelector('[data-view-count]');
|
||||
// Select all elements that are designated to display view counts.
|
||||
const viewCountElements = document.querySelectorAll('[data-view-count]');
|
||||
|
||||
if (viewCountElement) {
|
||||
const slug = viewCountElement.dataset.slug;
|
||||
|
||||
// Function to fetch and display the view count
|
||||
const getViews = async () => {
|
||||
try {
|
||||
const response = await fetch(`/api/views/${slug}`);
|
||||
if (!response.ok) throw new Error('Failed to fetch views');
|
||||
const data = await response.json();
|
||||
viewCountElement.textContent = `${data.count} views`;
|
||||
} catch (error) {
|
||||
console.error('Error getting views:', error);
|
||||
viewCountElement.textContent = 'Could not load views';
|
||||
}
|
||||
};
|
||||
|
||||
// Function to increment the view count
|
||||
const incrementView = async () => {
|
||||
try {
|
||||
// Use a flag in localStorage to prevent incrementing on every refresh
|
||||
const viewed = localStorage.getItem(`viewed-${slug}`);
|
||||
if (!viewed) {
|
||||
await fetch(`/api/views/${slug}`, { method: 'POST' });
|
||||
localStorage.setItem(`viewed-${slug}`, 'true');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error incrementing view:', error);
|
||||
}
|
||||
};
|
||||
|
||||
// Run the functions
|
||||
incrementView().then(getViews);
|
||||
// If no such elements are found, do nothing.
|
||||
if (viewCountElements.length === 0) {
|
||||
return;
|
||||
}
|
||||
});
|
||||
|
||||
// Process each view count element found on the page.
|
||||
viewCountElements.forEach(element => {
|
||||
const slug = element.dataset.slug;
|
||||
if (!slug) {
|
||||
// Skip if the element is missing the data-slug attribute.
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if the element is on a single post page by looking for a `.post-meta` parent.
|
||||
// This distinguishes a single post view (which should be counted) from a list view.
|
||||
const isSinglePost = element.closest('.post-meta');
|
||||
|
||||
if (isSinglePost) {
|
||||
// On a single post page, increment the view count.
|
||||
const viewed = localStorage.getItem(`viewed-${slug}`);
|
||||
|
||||
// Only increment if the user hasn't viewed this post in the current session.
|
||||
if (!viewed) {
|
||||
fetch(`/api/views/${slug}`, { method: 'POST' })
|
||||
.then(response => {
|
||||
if (!response.ok) throw new Error('Network response was not ok');
|
||||
return response.json();
|
||||
})
|
||||
.then(data => {
|
||||
element.textContent = data.count ?? 'Error';
|
||||
// Mark as viewed to prevent re-counting on refresh.
|
||||
localStorage.setItem(`viewed-${slug}`, 'true');
|
||||
})
|
||||
.catch(error => {
|
||||
console.error(`Error incrementing view count for slug ${slug}:`, error);
|
||||
element.textContent = 'N/A';
|
||||
});
|
||||
} else {
|
||||
// If already viewed in this session, just fetch the count without incrementing.
|
||||
fetch(`/api/views/${slug}`)
|
||||
.then(response => {
|
||||
if (!response.ok) throw new Error('Network response was not ok');
|
||||
return response.json();
|
||||
})
|
||||
.then(data => {
|
||||
element.textContent = data.count ?? 0;
|
||||
})
|
||||
.catch(error => {
|
||||
console.error(`Error fetching view count for slug ${slug}:`, error);
|
||||
element.textContent = 'N/A';
|
||||
});
|
||||
}
|
||||
} else {
|
||||
// On a list page (like the main blog page), just fetch and display the count.
|
||||
fetch(`/api/views/${slug}`)
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
element.textContent = data.count ?? 0;
|
||||
})
|
||||
.catch(error => {
|
||||
console.error(`Error fetching view count for slug ${slug}:`, error);
|
||||
element.textContent = 'N/A';
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
Reference in New Issue
Block a user