すたすた式
Blogger
Blogger用のランダムポストウィジェットを作成しました。
フィードを使ってすべての投稿からランダムにデータを取得しています。
投稿をまとめて表示するタイプと、ひとつずつ表示するタイプを作成しました。
HTML/JavaScriptガジェット(ウィジェット)での使用を想定しています。
すべての投稿からランダムにデータを取得しています。投稿数が少ないと投稿が重複して表示されるケースが増えます。
データを取得して表示するまでに時間がかかります。
「表示する投稿数」が多い場合、表示までかなり時間がかかります。
自分のテスト環境ではデータの取得に失敗するケースがやや多いように感じました。※
※fetchを使ってデータを取得しています。もしかしたらcallback関数を使うほうがいいのかも?
fetch
出力するHTMLは以下の通りです。「人気の投稿」ガジェットの構成に習いました。
<ul class="all-posts__post-list"> <li class="all-posts__post-list-item"> <figure class="all-posts__post-list-item-figure"> <a class="all-posts__post-list-item-anchor" href="..."> <img class="all-posts__post-list-item-img" width="..." height="..." alt="..." src="..."> <figcaption class="all-posts__post-list-item-figcaption">タイトル</figcaption> </a></figure> </li> </ul>
プログラムはHTML JavaScript CSSをまとめたものになります。
追記(2023年3月16日)CSSを一部変更。object-fit: coverを追加。
object-fit: cover
.all-posts__post-list-item-img { grid-area: thumnail; width: 72px; height: 72px; object-fit: cover; }
<div id="allPostsLoader" class="all-posts__loader"></div> <div id="allPosts" class="all-posts"></div> <script> /*! Copyright:2023 sutajp | Released under the MIT license | https://sutasutashiki.blogspot.com/p/mit-license.html */ //<![CDATA[ /** フィードですべての投稿をランダムに取得, HTMLで出力する */ "use strict"; /** * HTMLを生成して出力する関数 * @param {Array<object>} objects - [{title:string, url:string}] */ function createHtml(objects) { /** サムネイル設定 @type { {[key: string]: number | string} } */ const thumbnailSetting = { /** サムネイルの幅 @type {number} */ thumbnailWidth: 72, /** サムネイルの高さ @type {number} */ thumbnailHeight: 72, /** サムネイルの代替テキスト @type {string} */ thumbnailAlt: "ランダムピックアップサムネイル", }; /** ul要素 */ const ulElm = document.createElement("ul"); ulElm.className = "all-posts__post-list"; for (const outputObject of objects) { let title = outputObject.title; /** タグなどをエスケープしてサニタイジング(無害化)する */ if (/[<>"'`&]/g.test(outputObject.title)) { title = outputObject.title.replace(/[<>"'`&]/g, function (match) { return { "<": "<", ">": ">", '"': """, "'": "'", "`": "`", "&": "&", }[match]; }); } /** * テンプレートリテラルで記述 * {@link https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Template_literals MDN} */ const randomPostBody = `<li class="all-posts__post-list-item"> <figure class="all-posts__post-list-item-figure"> <a class="all-posts__post-list-item-anchor" href="${outputObject.url}"> <img class="all-posts__post-list-item-img" width="${thumbnailSetting.thumbnailWidth}" height="${thumbnailSetting.thumbnailHeight}" alt="${thumbnailSetting.thumbnailAlt}" src="${outputObject.thumbnail}"/> <figcaption class="all-posts__post-list-item-figcaption">${title}</figcaption> </a></figure></li>`; ulElm.insertAdjacentHTML("afterbegin", randomPostBody); } /** HTMLを出力 */ const allPosts = document.getElementById("allPosts"); allPosts.append(ulElm); /** HTMLを出力後, ローディング画面にdisplay:noneを設定*/ const allPostsLoader = document.getElementById("allPostsLoader"); allPostsLoader.style.display = "none"; } /** * フィードの取得に失敗したときに実行する関数 */ function errorHanding() { const allPosts = document.getElementById("allPosts"); allPosts.insertAdjacentHTML( "beforeend", "<span>データの取得に失敗しました🙁</span>" ); allPosts.style.minHeight = "50px"; /** ローディング画面にdisplay:noneを設定 */ const allPostsLoader = document.getElementById("allPostsLoader"); allPostsLoader.style.display = "none"; } //]]> /** * JSONデータを取得してオブジェクトを生成する関数 */ async function createRandomObjects() { /** 現在開いているページ @type {number} */ const currentPostId = "<data:view.postId/>"; //<![CDATA[ /** 表示数 @type {number} */ const numberOfDisplay = 5; /** サムネイル設定 @type { {[key: string]: number | string} } */ const thumbnailSetting = { /** サムネイルのパラメーターを置き換え @type {string} */ parameter1: "w72-h72-p-k-no-nu", /** サムネイルのパラメーターを置き換え @type {string} */ parameter2: "/w72-h72-p-k-no-nu/", /** Youtubeのサムネイル用 */ parameter3: "mqdefault.jpg", /** 記事に画像がない場合の代替画像 * @type {string} * {@link https://icooon-mono.com/11396-ウィンドウアイコン/ SVGを使用} * {@link https://icooon-mono.com/license/ ライセンス} * {@link https://heyallan.github.io/svg-to-data-uri/ Data URIにエンコード} */ noImage: "data:image/svg+xml,%3Csvg id='_x32_' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' x='0px' y='0px' viewBox='0 0 512 512' style='width: 128px; height: 128px; opacity: 1;' xml:space='preserve'%3E%3Cg%3E%3Cpath class='st0' d='M464,0H48C21.492,0,0,21.492,0,48v416c0,26.508,21.492,48,48,48h416c26.508,0,48-21.492,48-48V48 C512,21.492,490.508,0,464,0z M444.664,35c10.492,0,19,8.508,19,19s-8.508,19-19,19s-19-8.508-19-19S434.172,35,444.664,35z M374.164,35c10.492,0,19,8.508,19,19s-8.508,19-19,19s-19-8.508-19-19S363.672,35,374.164,35z M303.664,35 c10.492,0,19,8.508,19,19s-8.508,19-19,19s-19-8.508-19-19S293.172,35,303.664,35z M472,464c0,4.406-3.586,8-8,8H48 c-4.414,0-8-3.594-8-8V104h432V464z' fill='%234B4B4B'%3E%3C/path%3E%3Crect x='112' y='192' class='st0' width='288' height='32' fill='%234B4B4B'%3E%3C/rect%3E%3Crect x='112' y='272' class='st0' width='288' height='32' fill='%234B4B4B'%3E%3C/rect%3E%3Crect x='112' y='352' class='st0' width='152' height='32' fill='%234B4B4B'%3E%3C/rect%3E%3C/g%3E%3C/svg%3E", }; /** * 取得したデータを格納 * @type {Array<object>} - [{title:string, url:string}] */ let objects = []; /** * 投稿数 * @type {Number} - json.feed.openSearch$totalResults.$t */ let totalResults; /** 投稿数の取得の可否条件 @type {Boolean} */ let isFirstFeed = true; /** 投稿データの取得の可否条件 @type {Boolean} */ let isGetFeedObject = false; /** 重複チェック用配列 @type {Array} */ let duplicateCheck = []; /** startIndexの初期値 @type {number} */ let startIndex = 1; const maxResults = 1; /** ランダムにstartIndexを作成する関数 */ function createRandomStartIndex() { startIndex = Math.floor(Math.random() * totalResults + 1) + 1; duplicateCheck.push(startIndex); } do { /** 1回だけ重複チェック */ if (duplicateCheck.includes(startIndex)) { createRandomStartIndex(); } const feedUrl = "/feeds/posts/summary?alt=json&start-index=" + startIndex + "&max-results=" + maxResults; try { const response = await fetch(feedUrl); const json = await response.json(); /** 2回目以降は投稿データを取得 */ if (isGetFeedObject == true) { for (const jsonFeedEntry of json.feed.entry) { /** * 今開いているページかどうか判定用データ * jsonFeedEntry.id.$tで文字列を取得 * 例 tag:blogger.com,1999:blog-1234567890123456789.post-0149374603246402764 * 取得した文字列から正規表現でポストIDを取得 * @type {Array} - ["0149374603246402764"] */ const feedPostId = jsonFeedEntry.id.$t.match(/[0-9]+$/); /** サムネイルのurlを格納する変数を定義 @type {string} */ let thumbnailUrl; /** * 今開いているページのポストIDと判定用IDが違っていたら処理を実行 * (今開いているページのデータは取得しない) * 判定用IDはfeedPostIdの0番目の要素を使用 */ if (currentPostId != feedPostId[0]) { /** サムネイルがある場合 */ if ("media$thumbnail" in jsonFeedEntry) { /** サムネイルのurl @type {string} */ thumbnailUrl = jsonFeedEntry.media$thumbnail.url; /**サムネイルのパラメーター @enum {string} 正規表現 */ const thumbnailParameter = { parameter1: /s72-.*$/, parameter2: /\/s72-.*\//, parameter3: /default.jpg$/, }; /** サムネイルのパラメーターを置き換えるif文のブロック */ if (thumbnailParameter.parameter1.test(thumbnailUrl)) { thumbnailUrl = thumbnailUrl.replace( thumbnailParameter.parameter1, thumbnailSetting.parameter1 ); } else if (thumbnailParameter.parameter2.test(thumbnailUrl)) { thumbnailUrl = thumbnailUrl.replace( thumbnailParameter.parameter2, thumbnailSetting.parameter2 ); } else if (thumbnailParameter.parameter3.test(thumbnailUrl)) { thumbnailUrl = thumbnailUrl.replace( thumbnailParameter.parameter3, thumbnailSetting.parameter3 ); } } else { /** サムネイルがない場合の代替画像 */ thumbnailUrl = thumbnailSetting.noImage; } } for (const jsonFeedEntryLink of jsonFeedEntry.link) { if (jsonFeedEntryLink.rel == "alternate") { const feedObject = {}; feedObject.title = jsonFeedEntry.title.$t; feedObject.url = jsonFeedEntryLink.href; feedObject.thumbnail = thumbnailUrl; objects.push(feedObject); } } } } /** 1回目は投稿数を取得 */ if (isFirstFeed == true) { totalResults = json.feed.openSearch$totalResults.$t; /** 2回目以降は投稿数を取得しない */ isFirstFeed = false; /** 2回目以降はフィードオブジェクトを取得する */ isGetFeedObject = true; } createRandomStartIndex(); } catch (error) { console.error("フィードの取得に失敗しました"); errorHanding(); return; } } while (objects.length < numberOfDisplay); /** createHtml関数を実行 */ createHtml(objects); } createRandomObjects(); //]]> </script> <style> .all-posts { min-height: 465px; } .all-posts__post-list-item-anchor { display: grid; grid-template-rows: 72px; grid-template-columns: 25% 1fr; grid-template-areas: "thumnail title"; } .all-posts__post-list-item-img { grid-area: thumnail; width: 72px; height: 72px; object-fit: cover; } .all-posts__post-list-item-figcaption { grid-area: title; } /* https://projects.lukehaas.me/css-loaders/ */ .all-posts__loader { color: #808080; font-size: 12px; margin: auto; width: 1em; height: 1em; top: 100px; left: 0; right: 0; border-radius: 50%; position: relative; -webkit-animation: load4 1.3s infinite linear; animation: load4 1.3s infinite linear; -webkit-transform: translateZ(0); -ms-transform: translateZ(0); transform: translateZ(0); } @-webkit-keyframes load4 { 0%, 100% { box-shadow: 0 -3em 0 0.2em, 2em -2em 0 0em, 3em 0 0 -1em, 2em 2em 0 -1em, 0 3em 0 -1em, -2em 2em 0 -1em, -3em 0 0 -1em, -2em -2em 0 0; } 12.5% { box-shadow: 0 -3em 0 0, 2em -2em 0 0.2em, 3em 0 0 0, 2em 2em 0 -1em, 0 3em 0 -1em, -2em 2em 0 -1em, -3em 0 0 -1em, -2em -2em 0 -1em; } 25% { box-shadow: 0 -3em 0 -0.5em, 2em -2em 0 0, 3em 0 0 0.2em, 2em 2em 0 0, 0 3em 0 -1em, -2em 2em 0 -1em, -3em 0 0 -1em, -2em -2em 0 -1em; } 37.5% { box-shadow: 0 -3em 0 -1em, 2em -2em 0 -1em, 3em 0em 0 0, 2em 2em 0 0.2em, 0 3em 0 0em, -2em 2em 0 -1em, -3em 0em 0 -1em, -2em -2em 0 -1em; } 50% { box-shadow: 0 -3em 0 -1em, 2em -2em 0 -1em, 3em 0 0 -1em, 2em 2em 0 0em, 0 3em 0 0.2em, -2em 2em 0 0, -3em 0em 0 -1em, -2em -2em 0 -1em; } 62.5% { box-shadow: 0 -3em 0 -1em, 2em -2em 0 -1em, 3em 0 0 -1em, 2em 2em 0 -1em, 0 3em 0 0, -2em 2em 0 0.2em, -3em 0 0 0, -2em -2em 0 -1em; } 75% { box-shadow: 0em -3em 0 -1em, 2em -2em 0 -1em, 3em 0em 0 -1em, 2em 2em 0 -1em, 0 3em 0 -1em, -2em 2em 0 0, -3em 0em 0 0.2em, -2em -2em 0 0; } 87.5% { box-shadow: 0em -3em 0 0, 2em -2em 0 -1em, 3em 0 0 -1em, 2em 2em 0 -1em, 0 3em 0 -1em, -2em 2em 0 0, -3em 0em 0 0, -2em -2em 0 0.2em; } } @keyframes load4 { 0%, 100% { box-shadow: 0 -3em 0 0.2em, 2em -2em 0 0em, 3em 0 0 -1em, 2em 2em 0 -1em, 0 3em 0 -1em, -2em 2em 0 -1em, -3em 0 0 -1em, -2em -2em 0 0; } 12.5% { box-shadow: 0 -3em 0 0, 2em -2em 0 0.2em, 3em 0 0 0, 2em 2em 0 -1em, 0 3em 0 -1em, -2em 2em 0 -1em, -3em 0 0 -1em, -2em -2em 0 -1em; } 25% { box-shadow: 0 -3em 0 -0.5em, 2em -2em 0 0, 3em 0 0 0.2em, 2em 2em 0 0, 0 3em 0 -1em, -2em 2em 0 -1em, -3em 0 0 -1em, -2em -2em 0 -1em; } 37.5% { box-shadow: 0 -3em 0 -1em, 2em -2em 0 -1em, 3em 0em 0 0, 2em 2em 0 0.2em, 0 3em 0 0em, -2em 2em 0 -1em, -3em 0em 0 -1em, -2em -2em 0 -1em; } 50% { box-shadow: 0 -3em 0 -1em, 2em -2em 0 -1em, 3em 0 0 -1em, 2em 2em 0 0em, 0 3em 0 0.2em, -2em 2em 0 0, -3em 0em 0 -1em, -2em -2em 0 -1em; } 62.5% { box-shadow: 0 -3em 0 -1em, 2em -2em 0 -1em, 3em 0 0 -1em, 2em 2em 0 -1em, 0 3em 0 0, -2em 2em 0 0.2em, -3em 0 0 0, -2em -2em 0 -1em; } 75% { box-shadow: 0em -3em 0 -1em, 2em -2em 0 -1em, 3em 0em 0 -1em, 2em 2em 0 -1em, 0 3em 0 -1em, -2em 2em 0 0, -3em 0em 0 0.2em, -2em -2em 0 0; } 87.5% { box-shadow: 0em -3em 0 0, 2em -2em 0 -1em, 3em 0 0 -1em, 2em 2em 0 -1em, 0 3em 0 -1em, -2em 2em 0 0, -3em 0em 0 0, -2em -2em 0 0.2em; } } </style>
ひとつずつ表示するタイプは要素をひとつずつDOMへ追加しています。
したがってその都度、画面の位置調整などの計算が発生する場合もあります。
パフォーマンス的にあまりよろしくない気がします(感想)
JavaScript | 複数のノードをまとめて追加(DocumentFragment)
<div id="allPostsLoader" class="all-posts__loader"></div> <ul id="allPosts" class="all-posts"></ul> <script> /*! Copyright:2023 sutajp | Released under the MIT license | https://sutasutashiki.blogspot.com/p/mit-license.html */ //<![CDATA[ /** フィードですべての投稿をランダムに取得, HTMLで出力する */ "use strict"; /** * HTMLを生成して出力する関数 * @param {Array<object>} objects - [{title:string, url:string}] * @param {number} numberOfDisplay */ function createHtml(objects) { /** サムネイル設定 @type { {[key: string]: number | string} } */ const thumbnailSetting = { /** サムネイルの幅 @type {number} */ thumbnailWidth: 72, /** サムネイルの高さ @type {number} */ thumbnailHeight: 72, /** サムネイルの代替テキスト @type {string} */ thumbnailAlt: "ランダムピックアップサムネイル", }; /** HTMLの出力先 */ const allPosts = document.getElementById("allPosts"); for (const outputObject of objects) { let title = outputObject.title; /** タグなどをエスケープしてサニタイジング(無害化)する */ if (/[<>"'`&]/g.test(outputObject.title)) { title = outputObject.title.replace(/[<>"'`&]/g, function (match) { return { "<": "<", ">": ">", '"': """, "'": "'", "`": "`", "&": "&", }[match]; }); } /** * テンプレートリテラルで記述 * {@link https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Template_literals MDN} */ const randomPostBody = `<li class="all-posts__post-list-item"> <figure class="all-posts__post-list-item-figure"> <a class="all-posts__post-list-item-anchor" href="${outputObject.url}"> <img class="all-posts__post-list-item-img" width="${thumbnailSetting.thumbnailWidth}" height="${thumbnailSetting.thumbnailHeight}" alt="${thumbnailSetting.thumbnailAlt}" src="${outputObject.thumbnail}"/> <figcaption class="all-posts__post-list-item-figcaption">${title}</figcaption> </a></figure></li>`; allPosts.insertAdjacentHTML("afterbegin", randomPostBody); } } /** * フィードの取得に失敗したときに実行する関数 */ function errorHanding() { const allPosts = document.getElementById("allPosts"); allPosts.insertAdjacentHTML( "beforeend", "<span>データの取得に失敗しました🙁</span>" ); /** ローディング画面にdisplay:noneを設定*/ const allPostsLoader = document.getElementById("allPostsLoader"); allPostsLoader.style.display = "none"; } //]]> /** * JSONデータを取得してオブジェクトを生成する関数 */ async function createRandomObjects() { /** 現在開いているページ @type {number} */ const currentPostId = "<data:view.postId/>"; //<![CDATA[ /** 表示数 @type {number} */ const numberOfDisplay = 5; /** サムネイル設定 @type { {[key: string]: number | string} } */ const thumbnailSetting = { /** サムネイルのパラメーターを置き換え @type {string} */ parameter1: "w72-h72-p-k-no-nu", /** サムネイルのパラメーターを置き換え @type {string} */ parameter2: "/w72-h72-p-k-no-nu/", /** Youtubeのサムネイル用 */ parameter3: "mqdefault.jpg", /** 記事に画像がない場合の代替画像 * @type {string} * {@link https://icooon-mono.com/11396-ウィンドウアイコン/ SVGを使用} * {@link https://icooon-mono.com/license/ ライセンス} * {@link https://heyallan.github.io/svg-to-data-uri/ Data URIにエンコード} */ noImage: "data:image/svg+xml,%3Csvg id='_x32_' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' x='0px' y='0px' viewBox='0 0 512 512' style='width: 128px; height: 128px; opacity: 1;' xml:space='preserve'%3E%3Cg%3E%3Cpath class='st0' d='M464,0H48C21.492,0,0,21.492,0,48v416c0,26.508,21.492,48,48,48h416c26.508,0,48-21.492,48-48V48 C512,21.492,490.508,0,464,0z M444.664,35c10.492,0,19,8.508,19,19s-8.508,19-19,19s-19-8.508-19-19S434.172,35,444.664,35z M374.164,35c10.492,0,19,8.508,19,19s-8.508,19-19,19s-19-8.508-19-19S363.672,35,374.164,35z M303.664,35 c10.492,0,19,8.508,19,19s-8.508,19-19,19s-19-8.508-19-19S293.172,35,303.664,35z M472,464c0,4.406-3.586,8-8,8H48 c-4.414,0-8-3.594-8-8V104h432V464z' fill='%234B4B4B'%3E%3C/path%3E%3Crect x='112' y='192' class='st0' width='288' height='32' fill='%234B4B4B'%3E%3C/rect%3E%3Crect x='112' y='272' class='st0' width='288' height='32' fill='%234B4B4B'%3E%3C/rect%3E%3Crect x='112' y='352' class='st0' width='152' height='32' fill='%234B4B4B'%3E%3C/rect%3E%3C/g%3E%3C/svg%3E", }; /** * 取得したデータを格納 * @type {Array<object>} - [{title:string, url:string}] */ let objects = []; /** * 投稿数 * @type {Number} - json.feed.openSearch$totalResults.$t */ let totalResults; /** 投稿数の取得の可否条件 @type {Boolean} */ let isFirstFeed = true; /** 投稿データの取得の可否条件 @type {Boolean} */ let isGetFeedObject = false; /** 重複チェック用配列 @type {Array} */ let duplicateCheck = []; /** startIndexの初期値 @type {number} */ let startIndex = 1; const maxResults = 1; /** do while文 条件式用のカウント @type {number} */ let count = 0; /** ランダムにstartIndexを作成する関数 */ function createRandomStartIndex() { startIndex = Math.floor(Math.random() * totalResults + 1) + 1; duplicateCheck.push(startIndex); } do { /** 1回だけ重複チェック */ if (duplicateCheck.includes(startIndex)) { createRandomStartIndex(); } const feedUrl = "/feeds/posts/summary?alt=json&start-index=" + startIndex + "&max-results=" + maxResults; try { const response = await fetch(feedUrl); const json = await response.json(); /** 2回目以降は投稿データを取得 */ if (isGetFeedObject == true) { for (const jsonFeedEntry of json.feed.entry) { /** * 今開いているページかどうか判定用データ * jsonFeedEntry.id.$tで文字列を取得 * 例 tag:blogger.com,1999:blog-1234567890123456789.post-0149374603246402764 * 取得した文字列から正規表現でポストIDを取得 * @type {Array} - ["0149374603246402764"] */ const feedPostId = jsonFeedEntry.id.$t.match(/[0-9]+$/); /** サムネイルのurlを定義 @type {string} */ let thumbnailUrl; /** * 今開いているページのポストIDと判定用IDが違っていたら処理を実行 * (今開いているページのデータは取得しない) * 判定用IDはfeedPostIdの0番目の要素を使用 */ if (currentPostId != feedPostId[0]) { /** サムネイルがある場合 */ if ("media$thumbnail" in jsonFeedEntry) { /** サムネイルのurl @type {string} */ thumbnailUrl = jsonFeedEntry.media$thumbnail.url; /**サムネイルのパラメーター @enum {string} 正規表現 */ const thumbnailParameter = { parameter1: /s72-.*$/, parameter2: /\/s72-.*\//, parameter3: /default.jpg$/, }; /** サムネイルのパラメーターを置き換えるif文のブロック */ if (thumbnailParameter.parameter1.test(thumbnailUrl)) { thumbnailUrl = thumbnailUrl.replace( thumbnailParameter.parameter1, thumbnailSetting.parameter1 ); } else if (thumbnailParameter.parameter2.test(thumbnailUrl)) { thumbnailUrl = thumbnailUrl.replace( thumbnailParameter.parameter2, thumbnailSetting.parameter2 ); } else if (thumbnailParameter.parameter3.test(thumbnailUrl)) { thumbnailUrl = thumbnailUrl.replace( thumbnailParameter.parameter3, thumbnailSetting.parameter3 ); } } else { /** サムネイルがない場合の代替画像 */ thumbnailUrl = thumbnailSetting.noImage; } } for (const jsonFeedEntryLink of jsonFeedEntry.link) { if (jsonFeedEntryLink.rel == "alternate") { const feedObject = {}; feedObject.title = jsonFeedEntry.title.$t; feedObject.url = jsonFeedEntryLink.href; feedObject.thumbnail = thumbnailUrl; objects.push(feedObject); /** createHtml関数を実行 */ createHtml(objects); objects.length = 0; count++; } } } } /** 1回目は投稿数を取得 */ if (isFirstFeed == true) { totalResults = json.feed.openSearch$totalResults.$t; /** 2回目以降は投稿数を取得しない */ isFirstFeed = false; /** 2回目以降はフィードオブジェクトを取得する */ isGetFeedObject = true; } createRandomStartIndex(); } catch (error) { console.error("フィードの取得に失敗しました"); errorHanding(); return; } } while (count < numberOfDisplay); /** ローディング画面にdisplay:noneを設定 */ const allPostsLoader = document.getElementById("allPostsLoader"); allPostsLoader.style.display = "none"; } createRandomObjects(); //]]> </script> <style> .all-posts { min-height: 465px; } .all-posts__post-list-item-anchor { display: grid; grid-template-rows: 72px; grid-template-columns: 30% 1fr; grid-template-areas: "thumnail title"; } .all-posts__post-list-item-img { grid-area: thumnail; width: 72px; height: 72px; object-fit: cover; } .all-posts__post-list-item-figcaption { grid-area: title; } /* https://projects.lukehaas.me/css-loaders/ */ .all-posts__loader { color: #808080; font-size: 12px; margin: auto; width: 1em; height: 1em; top: 100px; left: 0; right: 0; border-radius: 50%; position: relative; -webkit-animation: load4 1.3s infinite linear; animation: load4 1.3s infinite linear; -webkit-transform: translateZ(0); -ms-transform: translateZ(0); transform: translateZ(0); } @-webkit-keyframes load4 { 0%, 100% { box-shadow: 0 -3em 0 0.2em, 2em -2em 0 0em, 3em 0 0 -1em, 2em 2em 0 -1em, 0 3em 0 -1em, -2em 2em 0 -1em, -3em 0 0 -1em, -2em -2em 0 0; } 12.5% { box-shadow: 0 -3em 0 0, 2em -2em 0 0.2em, 3em 0 0 0, 2em 2em 0 -1em, 0 3em 0 -1em, -2em 2em 0 -1em, -3em 0 0 -1em, -2em -2em 0 -1em; } 25% { box-shadow: 0 -3em 0 -0.5em, 2em -2em 0 0, 3em 0 0 0.2em, 2em 2em 0 0, 0 3em 0 -1em, -2em 2em 0 -1em, -3em 0 0 -1em, -2em -2em 0 -1em; } 37.5% { box-shadow: 0 -3em 0 -1em, 2em -2em 0 -1em, 3em 0em 0 0, 2em 2em 0 0.2em, 0 3em 0 0em, -2em 2em 0 -1em, -3em 0em 0 -1em, -2em -2em 0 -1em; } 50% { box-shadow: 0 -3em 0 -1em, 2em -2em 0 -1em, 3em 0 0 -1em, 2em 2em 0 0em, 0 3em 0 0.2em, -2em 2em 0 0, -3em 0em 0 -1em, -2em -2em 0 -1em; } 62.5% { box-shadow: 0 -3em 0 -1em, 2em -2em 0 -1em, 3em 0 0 -1em, 2em 2em 0 -1em, 0 3em 0 0, -2em 2em 0 0.2em, -3em 0 0 0, -2em -2em 0 -1em; } 75% { box-shadow: 0em -3em 0 -1em, 2em -2em 0 -1em, 3em 0em 0 -1em, 2em 2em 0 -1em, 0 3em 0 -1em, -2em 2em 0 0, -3em 0em 0 0.2em, -2em -2em 0 0; } 87.5% { box-shadow: 0em -3em 0 0, 2em -2em 0 -1em, 3em 0 0 -1em, 2em 2em 0 -1em, 0 3em 0 -1em, -2em 2em 0 0, -3em 0em 0 0, -2em -2em 0 0.2em; } } @keyframes load4 { 0%, 100% { box-shadow: 0 -3em 0 0.2em, 2em -2em 0 0em, 3em 0 0 -1em, 2em 2em 0 -1em, 0 3em 0 -1em, -2em 2em 0 -1em, -3em 0 0 -1em, -2em -2em 0 0; } 12.5% { box-shadow: 0 -3em 0 0, 2em -2em 0 0.2em, 3em 0 0 0, 2em 2em 0 -1em, 0 3em 0 -1em, -2em 2em 0 -1em, -3em 0 0 -1em, -2em -2em 0 -1em; } 25% { box-shadow: 0 -3em 0 -0.5em, 2em -2em 0 0, 3em 0 0 0.2em, 2em 2em 0 0, 0 3em 0 -1em, -2em 2em 0 -1em, -3em 0 0 -1em, -2em -2em 0 -1em; } 37.5% { box-shadow: 0 -3em 0 -1em, 2em -2em 0 -1em, 3em 0em 0 0, 2em 2em 0 0.2em, 0 3em 0 0em, -2em 2em 0 -1em, -3em 0em 0 -1em, -2em -2em 0 -1em; } 50% { box-shadow: 0 -3em 0 -1em, 2em -2em 0 -1em, 3em 0 0 -1em, 2em 2em 0 0em, 0 3em 0 0.2em, -2em 2em 0 0, -3em 0em 0 -1em, -2em -2em 0 -1em; } 62.5% { box-shadow: 0 -3em 0 -1em, 2em -2em 0 -1em, 3em 0 0 -1em, 2em 2em 0 -1em, 0 3em 0 0, -2em 2em 0 0.2em, -3em 0 0 0, -2em -2em 0 -1em; } 75% { box-shadow: 0em -3em 0 -1em, 2em -2em 0 -1em, 3em 0em 0 -1em, 2em 2em 0 -1em, 0 3em 0 -1em, -2em 2em 0 0, -3em 0em 0 0.2em, -2em -2em 0 0; } 87.5% { box-shadow: 0em -3em 0 0, 2em -2em 0 -1em, 3em 0 0 -1em, 2em 2em 0 -1em, 0 3em 0 -1em, -2em 2em 0 0, -3em 0em 0 0, -2em -2em 0 0.2em; } } </style>
HTML/JavaScriptガジェットを追加、プログラムを貼り付けて保存します。
レイアウト
ガジェットを追加をクリック
HTML/JavaScriptガジェットを追加
プログラムを貼り付けて保存をクリック
画面右下のフロッピーディスクマーク(保存)をクリック
いくつか設定項目を設けました。
表示数
/** 表示数 @type {number} */ const numberOfDisplay = 5;
サムネイルの設定
/** サムネイル設定 @type { {[key: string]: number | string} } */ const thumbnailSetting = { /** サムネイルの幅 @type {number} */ thumbnailWidth: 72, /** サムネイルの高さ @type {number} */ thumbnailHeight: 72, /** サムネイルの代替テキスト @type {string} */ thumbnailAlt: "ランダムピックアップサムネイル", };
/** サムネイル設定 @type { {[key: string]: number | string} } */ const thumbnailSetting = { /** サムネイルのパラメーターを置き換え @type {string} */ parameter1: "w72-h72-p-k-no-nu", /** サムネイルのパラメーターを置き換え @type {string} */ parameter2: "/w72-h72-p-k-no-nu/", /** Youtubeのサムネイル用 */ parameter3: "mqdefault.jpg", /** 記事に画像がない場合の代替画像 * @type {string} * {@link https://icooon-mono.com/11396-ウィンドウアイコン/ SVGを使用} * {@link https://icooon-mono.com/license/ ライセンス} * {@link https://heyallan.github.io/svg-to-data-uri/ Data URIにエンコード} */ noImage: "data:image/svg+xml,%3Csvg id='_x32_' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' x='0px' y='0px' viewBox='0 0 512 512' style='width: 128px; height: 128px; opacity: 1;' xml:space='preserve'%3E%3Cg%3E%3Cpath class='st0' d='M464,0H48C21.492,0,0,21.492,0,48v416c0,26.508,21.492,48,48,48h416c26.508,0,48-21.492,48-48V48 C512,21.492,490.508,0,464,0z M444.664,35c10.492,0,19,8.508,19,19s-8.508,19-19,19s-19-8.508-19-19S434.172,35,444.664,35z M374.164,35c10.492,0,19,8.508,19,19s-8.508,19-19,19s-19-8.508-19-19S363.672,35,374.164,35z M303.664,35 c10.492,0,19,8.508,19,19s-8.508,19-19,19s-19-8.508-19-19S293.172,35,303.664,35z M472,464c0,4.406-3.586,8-8,8H48 c-4.414,0-8-3.594-8-8V104h432V464z' fill='%234B4B4B'%3E%3C/path%3E%3Crect x='112' y='192' class='st0' width='288' height='32' fill='%234B4B4B'%3E%3C/rect%3E%3Crect x='112' y='272' class='st0' width='288' height='32' fill='%234B4B4B'%3E%3C/rect%3E%3Crect x='112' y='352' class='st0' width='152' height='32' fill='%234B4B4B'%3E%3C/rect%3E%3C/g%3E%3C/svg%3E", };
ランダムポストが出力されるまでローディング画面を表示しています。
必要なければ以下を削除してください。
<div id="allPostsLoader" class="all-posts__loader"></div>
JavaScriptは2か所
/** HTMLを出力後, ローディング画面にdisplay:noneを設定*/ const allPostsLoader = document.getElementById("allPostsLoader"); allPostsLoader.style.display = "none";
/* https://projects.lukehaas.me/css-loaders/ */ 以下、すべて削除
サムネイルのパラメーターの初期値は「人気の投稿ガジェット」のパラメーターw72-h72-p-k-no-nuに準じました。
w72-h72-p-k-no-nu
好みのものに変更してください。
パラメーターについてはこちらが詳しいです。
Google/Blogger Image URL Parameters · GitHub
The Ultimate Guide to Customize and Edit Blogger (BlogSpot) Images
サイト内検索に使ってください 🐤
© 2015 すたすた式
Enjoy!👍
QooQ
コメントなし:
コメントを投稿