Friday, October 2, 2026

The Google gadget is back and running

 


It seems Google has finally gotten around to fixing its Blogs I Follow gadget! The original issue was that it had completely stopped updating. You can read my previous post to see the workaround I used while waiting for the folks at Google to address the problem.

Below is the code I used to bring back the official Blogs I Follow gadget while hiding the temporary HTML/JavaScript fix.

To keep things organized, I saved both code snippets in OpenOffice. That way, if Google's gadget ever breaks again, it’s a quick copy and paste to deploy the temporary fix, and another simple copy and paste to switch back once it's resolved.

Note: The only change you will need to make is replacing my followed blog URLs with your own. I detailed step-by-step instructions on how to do this in the post linked above. Also you do this at your own risk. 


 HTML

<div style="display: none;">

  <div id="blogger-bloglist-feed">
    <p style="font-size: 0.9em; color: #ffffff; margin: 0;">Loading blog updates...</p>
  </div>

  <style>
    .bloglist-item {
      margin-bottom: 14px !important;
      padding-bottom: 10px !important;
      border-bottom: 1px dashed rgba(255, 255, 255, 0.3) !important;
      font-family: inherit !important;
    }
    /* Blog Name */
    .bloglist-name {
      display: block !important;
      font-size: 1.05em !important;
      font-weight: bold !important;
      color: #ffffff !important;
      margin-bottom: 4px !important;
      line-height: 1.3 !important;
    }
    /* Article Post Title Link */
    a.bloglist-link,
    a.bloglist-link:visited {
      display: block !important;
      font-size: 1.0em !important;
      font-weight: normal !important;
      color: #ffffff !important;
      text-decoration: none !important;
      line-height: 1.3 !important;
    }
    a.bloglist-link:hover {
      text-decoration: underline !important;
    }
    /* Updated Date */
    .bloglist-date {
      font-size: 0.85em !important;
      color: #ffffff !important;
      margin-top: 3px !important;
      display: block !important;
      opacity: 0.9 !important;
    }
  </style>

  <script>
    (function() {
      var followedFeeds = [
        "http://ae5x.blogspot.com/feeds/posts/default",
        "https://www.kb6nu.com/feed/",
        "http://w2lj.blogspot.com/feeds/posts/default",
        "http://pa1b-qrp.blogspot.com/feeds/posts/default",
        "https://ve7sar.blogspot.com/feeds/posts/default",
        "https://swling.com/blog/feed/",
        "https://www.arrl.org/arrl.rss",
        "http://ve3clq.blogspot.com/feeds/posts/default",
        "https://wb3gck.com/feed/",
        "https://tommcquiggan.blogspot.com/feeds/posts/default",
        "http://ve3vn.blogspot.com/feeds/posts/default",
        "https://ke9v.net/feed.xml",
        "https://kg3v.com/feed/",
        "http://offgridham.com/feed/",
        "http://pe4bas.blogspot.com/feeds/posts/default",
        "https://qrper.com/feed/",
        "https://www.amateurradio.com/feed/",
        "https://k9zw.wordpress.com/feed/"
      ];

      var container = document.getElementById("blogger-bloglist-feed");
      var loadedCount = 0;
      var items = [];

      // Helper function to fetch feeds that rss2json fails to parse
      async function fetchXmlFallback(feedUrl) {
        try {
          var proxyUrl = "https://api.allorigins.win/get?url=" + encodeURIComponent(feedUrl);
          var res = await fetch(proxyUrl);
          var data = await res.json();
          if (!data.contents) return null;
          
          var parser = new DOMParser();
          var xmlDoc = parser.parseFromString(data.contents, "text/xml");
          var item = xmlDoc.querySelector("item") || xmlDoc.querySelector("entry");
          if (!item) return null;
          
          var title = item.querySelector("title") ? item.querySelector("title").textContent : "Untitled";
          var link = item.querySelector("link") ? item.querySelector("link").textContent : "#";
          
          if (!link || link.trim() === "") {
            link = item.querySelector("link") ? item.querySelector("link").getAttribute("href") : "#";
          }
          
          var dateStr = item.querySelector("pubDate") ? item.querySelector("pubDate").textContent :
                        (item.querySelector("published") ? item.querySelector("published").textContent :
                        (item.querySelector("updated") ? item.querySelector("updated").textContent : ""));
                        
          var blogTitle = (xmlDoc.querySelector("channel > title") ? xmlDoc.querySelector("channel > title").textContent :
                          (xmlDoc.querySelector("feed > title") ? xmlDoc.querySelector("feed > title").textContent : "Blog"));
                          
          var pubDateObj = new Date(dateStr);
          return {
            blogTitle: blogTitle,
            title: title,
            link: link,
            date: pubDateObj,
            dateStr: isNaN(pubDateObj) ? "" : pubDateObj.toLocaleDateString(undefined, { month: 'short', day: 'numeric' })
          };
        } catch (e) {
          return null;
        }
      }

      followedFeeds.forEach(function(feedUrl) {
        var apiUrl = "https://api.rss2json.com/v1/api.json?rss_url=" + encodeURIComponent(feedUrl);
        fetch(apiUrl)
          .then(function(res) { return res.json(); })
          .then(async function(data) {
            if (data.status === "ok" && data.items && data.items.length > 0) {
              var blogTitle = data.feed.title || "Blog";
              var latestPost = data.items[0];
              var pubDateObj = new Date(latestPost.pubDate);
              items.push({
                blogTitle: blogTitle,
                title: latestPost.title,
                link: latestPost.link,
                date: pubDateObj,
                dateStr: isNaN(pubDateObj) ? "" : pubDateObj.toLocaleDateString(undefined, { month: 'short', day: 'numeric' })
              });
            } else {
              var fallbackItem = await fetchXmlFallback(feedUrl);
              if (fallbackItem) items.push(fallbackItem);
            }
          })
          .catch(async function(err) {
            var fallbackItem = await fetchXmlFallback(feedUrl);
            if (fallbackItem) items.push(fallbackItem);
          })
          .finally(function() {
            loadedCount++;
            if (loadedCount === followedFeeds.length) {
              if (items.length > 0) {
                items.sort(function(a, b) { return b.date - a.date; });
                var htmlBuffer = "";
                items.forEach(function(item) {
                  htmlBuffer += '<div class="bloglist-item">';
                  htmlBuffer += '<span class="bloglist-name">' + item.blogTitle + '</span>';
                  htmlBuffer += '<a class="bloglist-link" href="' + item.link + '" target="_blank" rel="noopener">' + item.title + '</a>';
                  if (item.dateStr) {
                    htmlBuffer += '<span class="bloglist-date">Updated: ' + item.dateStr + '</span>';
                  }
                  htmlBuffer += '</div>';
                });
                container.innerHTML = htmlBuffer;
              } else {
                container.innerHTML = "<p style='font-size:0.85em; color: #ffffff;'>No recent updates available.</p>";
              }
            }
          });
      });
    })();
  </script>

</div>

4 comments:

John AE5X said...

Excellent new - thanks for the update, Mike (and for the fix during the down-time).

73,
John

VE9KK said...

Good afternoon John, yes I was checking some blogger blogs to see if their blog they follow were updating. For about a week I found the updates to be solid and consistent. So I flipped back but still have the other HTML saved in an open office doc. Glad to help and have a great weekend.
73,
Mike
VE9KK

PE4BAS, Bas said...

Glad everything is working again. Your workaround was interesting... 73, Bas

Anonymous said...

Good morning Bas, yes it was a nice way to keep the blogs that I follow updated.
73,
Mike