PageSourceSearch

https://o-ki.co.jp/common/js/star-rating.js

js o-ki.co.jp collected 2026-10-04 19:10:42 UTC 4,714 bytes, 202 lines download raw bytes

1document.addEventListener("DOMContentLoaded", function () {
2  const CONFIG = {
3    // 自社PHP API
4    proxyUrl: "https://o-ki.co.jp/cgi-bin/get_star-rating.cgi",
5
6    // 商品一覧の商品ブロック
7    targetSelector: ".column4, .column5",
8
9    // レビュー表示位置
10    insertPosition: ".price, .sp_price",
11
12    // 同時取得数
13    maxConcurrent: 3,
14
15    // 未キャッシュ時の取得間隔(ms)
16    requestInterval: 1100,
17  };
18
19  const queue = [];
20  let activeRequests = 0;
21
22  // 表示領域に近づいた商品だけ取得する
23  const observer = new IntersectionObserver(
24    (entries) => {
25      entries.forEach((entry) => {
26        if (!entry.isIntersecting) return;
27
28        const element = entry.target;
29        const itemCode = getItemCode(element);
30
31        if (itemCode) {
32          enqueueFetch(itemCode, element);
33        }
34
35        observer.unobserve(element);
36      });
37    },
38    {
39      rootMargin: "100px",
40    },
41  );
42
43  // 外部から再実行できるようにする
44  window.initStarRating = function (targets) {
45    const elements = targets || document.querySelectorAll(CONFIG.targetSelector);
46
47    elements.forEach((el) => {
48      if (!el.querySelector(".sps-review-rating")) {
49        observer.observe(el);
50      }
51    });
52  };
53
54  // 商品コード取得
55  function getItemCode(element) {
56    const anchor = element.querySelector("h2 a, h3 a");
57
58    if (!anchor) return null;
59
60    const href = anchor.getAttribute("href");
61
62    if (!href) return null;
63
64    const match = href.match(/\/SHOP\/(.+)\.html/);
65
66    return match ? match[1] : null;
67  }
68
69  // キュー追加
70  function enqueueFetch(itemCode, element) {
71    const cacheKey = "sps_review_" + itemCode;
72
73    const cachedData = sessionStorage.getItem(cacheKey);
74
75    // ページ内キャッシュがあれば即表示
76    if (cachedData) {
77      try {
78        const data = JSON.parse(cachedData);
79
80        displayReview(element, data.score, data.count);
81
82        return;
83      } catch (e) {
84        sessionStorage.removeItem(cacheKey);
85      }
86    }
87
88    queue.push({
89      itemCode,
90      element,
91    });
92
93    processQueue();
94  }
95
96  // API取得処理
97  async function processQueue() {
98    if (activeRequests >= CONFIG.maxConcurrent || queue.length === 0) {
99      return;
100    }
101
102    activeRequests++;
103
104    const { itemCode, element } = queue.shift();
105
106    try {
107      const url = `${CONFIG.proxyUrl}?item_code=${encodeURIComponent(itemCode)}`;
108
109      const response = await fetch(url);
110
111      if (response.ok) {
112        const data = await response.json();
113
114        sessionStorage.setItem("sps_review_" + itemCode, JSON.stringify(data));
115
116        displayReview(element, data.score, data.count);
117      }
118
119      activeRequests--;
120
121      // 次の取得
122      setTimeout(() => {
123        processQueue();
124      }, CONFIG.requestInterval);
125    } catch (error) {
126      activeRequests--;
127
128      setTimeout(() => {
129        processQueue();
130      }, CONFIG.requestInterval);
131    }
132  }
133
134  // レビュー表示
135  function displayReview(element, score, count) {
136    if (element.querySelector(".sps-review-rating")) {
137      return;
138    }
139
140    const priceElement = element.querySelector(CONFIG.insertPosition);
141
142    if (!priceElement) return;
143
144    const container = document.createElement("div");
145
146    container.className = "sps-review-rating";
147
148    container.innerHTML = createReviewHtml(score, count);
149
150    priceElement.insertAdjacentElement("afterend", container);
151  }
152
153  // 表示HTML作成
154  function createReviewHtml(score, count) {
155    score = Number(score) || 0;
156
157    count = Number(count) || 0;
158
159    const fillWidth = (score / 5) * 100;
160
161    const displayScore = score.toFixed(1);
162
163    return `
164
165            <div class="box-star">
166
167                <div class="sps-stars-outer">
168
169                    ★★★★★
170
171                    <div class="sps-stars-inner"
172                         style="width:${fillWidth}%;">
173                        ★★★★★
174                    </div>
175
176                </div>
177
178
179                <div class="sps-stars-num">
180
181                    <span class="sps-review-text">
182                        ${displayScore}
183                    </span>
184
185                    <span class="sps-review-count">
186                        ${count}ä»¶
187                    </span>
188
189                </div>
190
191            </div>
192
193        `;
194  }
195
196  // ページ読み込み後開始
197  window.addEventListener("load", () => {
198    setTimeout(() => {
199      window.initStarRating();
200    }, 800);
201  });
202});

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.