Customizing the Init View Count Shortcode to Create a Tabbed Post Ranking Board

This article guides you through building a post ranking board based on view count, with a tabbed interface to switch between time ranges like Today, This Week, This Month, and All Time. The interface uses UIkit, and the data is fetched directly from the Init View Count REST API.

Customizing the Init View Count Shortcode to Create a Tabbed Post Ranking Board

Demo

Update: Since version 1.3, Init View Count includes built-in support for tabbed post ranking via shortcode. If you just want a ready-to-use, efficient UI without relying on external frameworks, the shortcode is the recommended solution. The tutorial below is only needed if you want full UI control using UIkit or any other CSS framework.

Requirements

  • The Init View Count plugin is installed and collecting real view data.
  • Your theme supports UIkit (at least uk-tab, uk-switcher, uk-grid classes).
  • You are comfortable adding JavaScript to your theme or plugin (via wp_enqueue_script or inline scripts).

1. Create the HTML structure for the ranking board

Start by building a simple HTML layout using uk-tab for the time range tabs and uk-switcher to toggle the associated content.

<h3>Demo</h3>
<div id="view-ranking" class="uk-margin">
  <ul class="uk-tab" data-uk-tab>
    <li class="uk-active"><a href="#" data-range="total">All Time</a></li>
    <li><a href="#" data-range="day">Today</a></li>
    <li><a href="#" data-range="week">This Week</a></li>
    <li><a href="#" data-range="month">This Month</a></li>
  </ul>

  <ul class="uk-switcher uk-margin">
    <li>
      <div class="ranking-content" data-range="total" data-loaded="false">
        <!-- Skeleton for the first tab only -->
        <div class="uk-grid-small uk-flex-middle uk-margin-small" uk-grid>
          <div class="uk-width-auto">
            <div class="uk-background-muted uk-border-rounded" style="width:60px; height:60px;"></div>
          </div>
          <div class="uk-width-expand">
            <div class="uk-margin-small">
              <div class="uk-background-muted uk-border-rounded" style="height: 16px; width: 80%;"></div>
            </div>
            <div class="uk-margin-small-top">
              <div class="uk-background-muted uk-border-rounded" style="height: 12px; width: 60%;"></div>
            </div>
          </div>
        </div>

        <div class="uk-grid-small uk-flex-middle uk-margin-small" uk-grid>
          <div class="uk-width-auto">
            <div class="uk-background-muted uk-border-rounded" style="width:60px; height:60px;"></div>
          </div>
          <div class="uk-width-expand">
            <div class="uk-margin-small">
              <div class="uk-background-muted uk-border-rounded" style="height: 16px; width: 80%;"></div>
            </div>
            <div class="uk-margin-small-top">
              <div class="uk-background-muted uk-border-rounded" style="height: 12px; width: 60%;"></div>
            </div>
          </div>
        </div>

        <div class="uk-grid-small uk-flex-middle uk-margin-small" uk-grid>
          <div class="uk-width-auto">
            <div class="uk-background-muted uk-border-rounded" style="width:60px; height:60px;"></div>
          </div>
          <div class="uk-width-expand">
            <div class="uk-margin-small">
              <div class="uk-background-muted uk-border-rounded" style="height: 16px; width: 80%;"></div>
            </div>
            <div class="uk-margin-small-top">
              <div class="uk-background-muted uk-border-rounded" style="height: 12px; width: 60%;"></div>
            </div>
          </div>
        </div>

        <div class="uk-grid-small uk-flex-middle uk-margin-small" uk-grid>
          <div class="uk-width-auto">
            <div class="uk-background-muted uk-border-rounded" style="width:60px; height:60px;"></div>
          </div>
          <div class="uk-width-expand">
            <div class="uk-margin-small">
              <div class="uk-background-muted uk-border-rounded" style="height: 16px; width: 80%;"></div>
            </div>
            <div class="uk-margin-small-top">
              <div class="uk-background-muted uk-border-rounded" style="height: 12px; width: 60%;"></div>
            </div>
          </div>
        </div>

        <div class="uk-grid-small uk-flex-middle uk-margin-small" uk-grid>
          <div class="uk-width-auto">
            <div class="uk-background-muted uk-border-rounded" style="width:60px; height:60px;"></div>
          </div>
          <div class="uk-width-expand">
            <div class="uk-margin-small">
              <div class="uk-background-muted uk-border-rounded" style="height: 16px; width: 80%;"></div>
            </div>
            <div class="uk-margin-small-top">
              <div class="uk-background-muted uk-border-rounded" style="height: 12px; width: 60%;"></div>
            </div>
          </div>
        </div>
      </div>
    </li>
    <li>
      <div class="ranking-content" data-range="day" data-loaded="false"></div>
    </li>
    <li>
      <div class="ranking-content" data-range="week" data-loaded="false"></div>
    </li>
    <li>
      <div class="ranking-content" data-range="month" data-loaded="false"></div>
    </li>
  </ul>
</div>

The first tab (All Time) includes static skeleton loading blocks directly in the HTML to prevent layout shifting while the data is loading.

2. Add JavaScript to fetch and display data from the REST API

Next, write JavaScript to call the /wp-json/initvico/v1/top API, which returns the most viewed posts for each time range. Each tab only loads data when the user clicks it, helping reduce unnecessary API calls and improve performance.

document.addEventListener('DOMContentLoaded', function () {
  const container = document.getElementById('view-ranking');
  if (!container) return;

  const tabs = container.querySelectorAll('.uk-tab a');
  const contents = container.querySelectorAll('.ranking-content');
  const cache = {};

  function renderItem(item) {
    return `
      <div class="uk-grid-small uk-flex-middle uk-margin-small" uk-grid>
        <div class="uk-width-auto">
          <a href="${item.link}">
            <img src="${item.thumbnail}" alt="${item.title}" width="60" height="60" style="object-fit:cover;" class="uk-border-rounded">
          </a>
        </div>
        <div class="uk-width-expand">
          <h5 class="uk-margin-remove">
            <a href="${item.link}" class="uk-link-heading">${item.title}</a>
          </h5>
          <div class="uk-text-meta">${item.views.toLocaleString()} views · ${item.date}</div>
        </div>
      </div>
    `;
  }

  function renderLoading() {
    let skeleton = '';
    for (let i = 0; i < 5; i++) {
      skeleton += `
        <div class="uk-grid-small uk-flex-middle uk-margin-small" uk-grid>
          <div class="uk-width-auto">
            <div class="uk-background-muted uk-border-rounded" style="width:60px; height:60px;"></div>
          </div>
          <div class="uk-width-expand">
            <div class="uk-margin-small">
              <div class="uk-background-muted uk-border-rounded" style="height: 16px; width: 80%;"></div>
            </div>
            <div class="uk-margin-small-top">
              <div class="uk-background-muted uk-border-rounded" style="height: 12px; width: 60%;"></div>
            </div>
          </div>
        </div>
      `;
    }
    return skeleton;
  }

  function loadRanking(range, target) {
    if (cache[range]) {
      target.innerHTML = cache[range];
      return;
    }

    // Only show loading state for non-initial tabs
    if (range !== 'total') {
      target.innerHTML = renderLoading();
    }

    fetch(`/wp-json/initvico/v1/top?range=${range}&number=5`)
      .then(res => res.json())
      .then(data => {
        if (!Array.isArray(data)) return;
        const html = data.map(renderItem).join('');
        cache[range] = html;
        target.innerHTML = html || '<div class="uk-text-muted">No data available.</div>';
        target.dataset.loaded = "true";
      })
      .catch(() => {
        target.innerHTML = '<div class="uk-text-danger">Failed to load data.</div>';
      });
  }

  // Map data-range => content element
  const contentMap = {};
  contents.forEach(content => {
    const range = content.dataset.range;
    if (range) contentMap[range] = content;
  });

  // Auto-load the default tab ("All Time")
  const firstRange = container.querySelector('.uk-tab .uk-active a')?.dataset.range;
  if (firstRange && contentMap[firstRange]) {
    loadRanking(firstRange, contentMap[firstRange]);
  }

  // Load on tab switch
  tabs.forEach(tab => {
    tab.addEventListener('click', function () {
      const range = this.dataset.range;
      const target = contentMap[range];
      if (!target || target.dataset.loaded === "true") return;
      loadRanking(range, target);
    });
  });
});

The first tab will skip rendering skeletons via JavaScript, since they’re already included in the HTML. Other tabs will display a loading state dynamically using a JavaScript function before replacing it with actual data.

Note for non-UIkit users

If you’re using a different CSS framework like Bootstrap, Tailwind, or custom CSS, feel free to replace UIkit classes such as uk-tab, uk-switcher, uk-grid, and uk-text-meta with ones that match your setup. As long as the HTML structure remains intact, you can fully adapt the styling to your own design system.

Conclusion

With Init View Count and a bit of JavaScript, you can build a beautiful, flexible, and high-performance post ranking board. This is a practical example of how to use the plugin’s REST API to power a modern frontend without relying on PHP shortcodes.

Comments


  • No comments yet.

Init Toolbox

Press Ctrl + \ on desktop, or swipe left anywhere on mobile.

Login