feat: 新增 icons.region 配置项&修改 favicon 加载超时机制&修复去除硬编码
- 新增 icons.region: com | cn 配置项,允许用户选择优先使用国内源或国外源 - com: 优先 gstatic.com,失败回退 gstatic.cn - cn: 优先 gstatic.cn,失败回退 gstatic.com - 修改 favicon 加载超时判断机制 - 自定义 faviconUrl: 5秒超时后显示回退图标 - 自动 favicon: 每次尝试3秒超时,最多等待6秒 - 更新配置文档和默认配置示例 - 去除卡片模板中的url硬编码 Issue: #31
This commit is contained in:
14
README.md
14
README.md
@@ -43,6 +43,20 @@
|
|||||||
<details>
|
<details>
|
||||||
<summary>点击查看/隐藏更新日志</summary>
|
<summary>点击查看/隐藏更新日志</summary>
|
||||||
|
|
||||||
|
### 2026/01/03
|
||||||
|
|
||||||
|
关联 Issue:[#31](https://github.com/rbetree/menav/issues/31)
|
||||||
|
|
||||||
|
**1. favicon 加载优化**
|
||||||
|
|
||||||
|
- 新增 `icons.region: com | cn` 配置项,允许用户选择优先使用国内源或国外源
|
||||||
|
- `com`(默认):优先 gstatic.com,失败回退 gstatic.cn
|
||||||
|
- `cn`:优先 gstatic.cn,失败回退 gstatic.com
|
||||||
|
- 修改 favicon 加载超时判断机制
|
||||||
|
- 自定义 faviconUrl:5秒超时后显示回退图标
|
||||||
|
- 自动 favicon:每次尝试3秒超时,最多等待6秒
|
||||||
|
- 避免网络慢时长时间显示加载动画
|
||||||
|
|
||||||
### 2026/01/02
|
### 2026/01/02
|
||||||
|
|
||||||
关联 Issue:[#30](https://github.com/rbetree/menav/issues/30)
|
关联 Issue:[#30](https://github.com/rbetree/menav/issues/30)
|
||||||
|
|||||||
@@ -106,6 +106,10 @@ MeNav 配置系统采用“完全替换”策略(不合并),按以下优
|
|||||||
- `icons.mode: favicon | manual`
|
- `icons.mode: favicon | manual`
|
||||||
- `favicon`:会请求第三方服务(Google)获取站点 favicon,失败自动回退到 Font Awesome 图标
|
- `favicon`:会请求第三方服务(Google)获取站点 favicon,失败自动回退到 Font Awesome 图标
|
||||||
- `manual`:完全使用手动 Font Awesome 图标,不发起外部请求(适合内网/离线/隐私敏感场景)
|
- `manual`:完全使用手动 Font Awesome 图标,不发起外部请求(适合内网/离线/隐私敏感场景)
|
||||||
|
- `icons.region: com | cn`(默认 `com`)
|
||||||
|
- `com`:优先使用 `gstatic.com`(国际版),失败后回退到 `gstatic.cn`(中国版)
|
||||||
|
- `cn`:优先使用 `gstatic.cn`(中国版),失败后回退到 `gstatic.com`(国际版)
|
||||||
|
- 说明:如果你在中国大陆且访问 gstatic.com 较慢,建议设置为 `cn` 以提升图标加载速度
|
||||||
- 站点级覆盖(可选,写在 `pages/*.yml` 的每个 `sites[]` 节点上):
|
- 站点级覆盖(可选,写在 `pages/*.yml` 的每个 `sites[]` 节点上):
|
||||||
- `faviconUrl`:为单个站点指定图标链接(可远程或本地相对路径;本地建议以 `assets/` 开头,构建会复制到 `dist/` 同路径),优先级最高
|
- `faviconUrl`:为单个站点指定图标链接(可远程或本地相对路径;本地建议以 `assets/` 开头,构建会复制到 `dist/` 同路径),优先级最高
|
||||||
- `forceIconMode: favicon | manual`:强制该站点使用指定模式(不设置则跟随全局 `icons.mode`)
|
- `forceIconMode: favicon | manual`:强制该站点使用指定模式(不设置则跟随全局 `icons.mode`)
|
||||||
@@ -116,7 +120,7 @@ MeNav 配置系统采用“完全替换”策略(不合并),按以下优
|
|||||||
- name: "Ant Design"
|
- name: "Ant Design"
|
||||||
url: "https://ant.design/"
|
url: "https://ant.design/"
|
||||||
icon: "fas fa-th"
|
icon: "fas fa-th"
|
||||||
forceIconMode: manual # 强制使用手动图标,绕过 favicon 默认“地球”图标
|
forceIconMode: manual # 强制使用手动图标,绕过 favicon 默认"地球"图标
|
||||||
- name: "Example"
|
- name: "Example"
|
||||||
url: "https://example.com/"
|
url: "https://example.com/"
|
||||||
faviconUrl: "https://example.com/favicon.png" # 单站点自定义 favicon
|
faviconUrl: "https://example.com/favicon.png" # 单站点自定义 favicon
|
||||||
|
|||||||
@@ -12,9 +12,15 @@ icons:
|
|||||||
# 站点卡片图标模式:
|
# 站点卡片图标模式:
|
||||||
# - favicon:自动根据 URL 加载站点 favicon(失败时回退到 Font Awesome 图标)
|
# - favicon:自动根据 URL 加载站点 favicon(失败时回退到 Font Awesome 图标)
|
||||||
# - manual:始终使用手动指定的 Font Awesome 图标(不发起外部请求)
|
# - manual:始终使用手动指定的 Font Awesome 图标(不发起外部请求)
|
||||||
# 隐私提示:启用 favicon 模式会请求第三方服务以获取图标,可能将站点 URL 发送给服务商(详见 README“隐私说明”)。
|
# 隐私提示:启用 favicon 模式会请求第三方服务以获取图标,可能将站点 URL 发送给服务商(详见 README"隐私说明")。
|
||||||
mode: favicon # 可选: favicon | manual(默认 favicon)
|
mode: favicon # 可选: favicon | manual(默认 favicon)
|
||||||
|
|
||||||
|
# favicon 服务区域选择(仅在 mode: favicon 时生效):
|
||||||
|
# - com:优先使用 gstatic.com(国际版),失败后回退到 gstatic.cn(中国版)
|
||||||
|
# - cn:优先使用 gstatic.cn(中国版),失败后回退到 gstatic.com(国际版)
|
||||||
|
# 说明:如果你在中国大陆且访问 gstatic.com 较慢,建议设置为 cn 以提升图标加载速度
|
||||||
|
region: cn # 可选: com | cn(默认 com)
|
||||||
|
|
||||||
# 字体设置:全站基础字体
|
# 字体设置:全站基础字体
|
||||||
# - source: css | google | system
|
# - source: css | google | system
|
||||||
# - css: 通过 cssUrl 引入第三方字体 CSS
|
# - css: 通过 cssUrl 引入第三方字体 CSS
|
||||||
|
|||||||
@@ -307,6 +307,8 @@ function ensureConfigDefaults(config) {
|
|||||||
result.icons = result.icons || {};
|
result.icons = result.icons || {};
|
||||||
// icons.mode: manual | favicon, 默认 favicon
|
// icons.mode: manual | favicon, 默认 favicon
|
||||||
result.icons.mode = result.icons.mode || 'favicon';
|
result.icons.mode = result.icons.mode || 'favicon';
|
||||||
|
// icons.region: com | cn, 默认 com(优先使用 gstatic.com,失败后回退到 gstatic.cn)
|
||||||
|
result.icons.region = result.icons.region || 'com';
|
||||||
|
|
||||||
// 站点基本信息默认值
|
// 站点基本信息默认值
|
||||||
result.site.title = result.site.title || 'MeNav导航';
|
result.site.title = result.site.title || 'MeNav导航';
|
||||||
|
|||||||
@@ -194,6 +194,48 @@ function add(a, b) {
|
|||||||
return numA + numB;
|
return numA + numB;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据 icons.region 配置生成 favicon URL
|
||||||
|
* @param {string} url 站点 URL
|
||||||
|
* @param {Object} options Handlebars options 对象
|
||||||
|
* @returns {string} favicon URL
|
||||||
|
* @example {{faviconUrl url}}
|
||||||
|
*/
|
||||||
|
function faviconUrl(url, options) {
|
||||||
|
if (!url) return '';
|
||||||
|
|
||||||
|
const region = options.data.root.icons?.region || 'com';
|
||||||
|
const domain = region === 'cn' ? 't3.gstatic.cn' : 't3.gstatic.com';
|
||||||
|
|
||||||
|
try {
|
||||||
|
const encodedUrl = encodeURIComponent(String(url));
|
||||||
|
return `https://${domain}/faviconV2?client=SOCIAL&type=FAVICON&fallback_opts=TYPE,SIZE,URL&url=${encodedUrl}&size=32`;
|
||||||
|
} catch (e) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据 icons.region 配置生成 favicon 回退 URL
|
||||||
|
* @param {string} url 站点 URL
|
||||||
|
* @param {Object} options Handlebars options 对象
|
||||||
|
* @returns {string} favicon 回退 URL
|
||||||
|
* @example {{faviconFallbackUrl url}}
|
||||||
|
*/
|
||||||
|
function faviconFallbackUrl(url, options) {
|
||||||
|
if (!url) return '';
|
||||||
|
|
||||||
|
const region = options.data.root.icons?.region || 'com';
|
||||||
|
const domain = region === 'cn' ? 't3.gstatic.com' : 't3.gstatic.cn';
|
||||||
|
|
||||||
|
try {
|
||||||
|
const encodedUrl = encodeURIComponent(String(url));
|
||||||
|
return `https://${domain}/faviconV2?client=SOCIAL&type=FAVICON&fallback_opts=TYPE,SIZE,URL&url=${encodedUrl}&size=32`;
|
||||||
|
} catch (e) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 导出所有工具类助手函数
|
// 导出所有工具类助手函数
|
||||||
module.exports = {
|
module.exports = {
|
||||||
slice,
|
slice,
|
||||||
@@ -205,5 +247,7 @@ module.exports = {
|
|||||||
pick,
|
pick,
|
||||||
keys,
|
keys,
|
||||||
encodeURIComponent: encodeURIComponentHelper,
|
encodeURIComponent: encodeURIComponentHelper,
|
||||||
add
|
add,
|
||||||
|
faviconUrl,
|
||||||
|
faviconFallbackUrl
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -392,14 +392,17 @@ window.MeNav = {
|
|||||||
contentWrapper.appendChild(descEl);
|
contentWrapper.appendChild(descEl);
|
||||||
|
|
||||||
let iconsMode = 'favicon';
|
let iconsMode = 'favicon';
|
||||||
|
let iconsRegion = 'com';
|
||||||
try {
|
try {
|
||||||
const cfg =
|
const cfg =
|
||||||
window.MeNav && typeof window.MeNav.getConfig === 'function'
|
window.MeNav && typeof window.MeNav.getConfig === 'function'
|
||||||
? window.MeNav.getConfig()
|
? window.MeNav.getConfig()
|
||||||
: null;
|
: null;
|
||||||
iconsMode = (cfg && (cfg.data?.icons?.mode || cfg.icons?.mode)) || 'favicon';
|
iconsMode = (cfg && (cfg.data?.icons?.mode || cfg.icons?.mode)) || 'favicon';
|
||||||
|
iconsRegion = (cfg && (cfg.data?.icons?.region || cfg.icons?.region)) || 'com';
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
iconsMode = 'favicon';
|
iconsMode = 'favicon';
|
||||||
|
iconsRegion = 'com';
|
||||||
}
|
}
|
||||||
|
|
||||||
const shouldUseCustomFavicon = Boolean(siteFaviconUrl);
|
const shouldUseCustomFavicon = Boolean(siteFaviconUrl);
|
||||||
@@ -422,11 +425,23 @@ window.MeNav = {
|
|||||||
favicon.src = siteFaviconUrl;
|
favicon.src = siteFaviconUrl;
|
||||||
favicon.alt = `${siteName} favicon`;
|
favicon.alt = `${siteName} favicon`;
|
||||||
favicon.loading = 'lazy';
|
favicon.loading = 'lazy';
|
||||||
|
|
||||||
|
// 超时处理:5秒后如果还没加载成功,显示回退图标
|
||||||
|
let loadTimeout = setTimeout(() => {
|
||||||
|
if (!favicon.classList.contains('loaded')) {
|
||||||
|
favicon.classList.add('error');
|
||||||
|
placeholder.classList.add('hidden');
|
||||||
|
fallback.classList.add('visible');
|
||||||
|
}
|
||||||
|
}, 5000);
|
||||||
|
|
||||||
favicon.addEventListener('load', () => {
|
favicon.addEventListener('load', () => {
|
||||||
|
clearTimeout(loadTimeout);
|
||||||
favicon.classList.add('loaded');
|
favicon.classList.add('loaded');
|
||||||
placeholder.classList.add('hidden');
|
placeholder.classList.add('hidden');
|
||||||
});
|
});
|
||||||
favicon.addEventListener('error', () => {
|
favicon.addEventListener('error', () => {
|
||||||
|
clearTimeout(loadTimeout);
|
||||||
favicon.classList.add('error');
|
favicon.classList.add('error');
|
||||||
placeholder.classList.add('hidden');
|
placeholder.classList.add('hidden');
|
||||||
fallback.classList.add('visible');
|
fallback.classList.add('visible');
|
||||||
@@ -437,8 +452,13 @@ window.MeNav = {
|
|||||||
iconContainer.appendChild(fallback);
|
iconContainer.appendChild(fallback);
|
||||||
iconWrapper.appendChild(iconContainer);
|
iconWrapper.appendChild(iconContainer);
|
||||||
} else if (effectiveIconsMode === 'favicon' && siteUrl && /^https?:\/\//i.test(siteUrl)) {
|
} else if (effectiveIconsMode === 'favicon' && siteUrl && /^https?:\/\//i.test(siteUrl)) {
|
||||||
const faviconUrlPrimary = `https://t3.gstatic.com/faviconV2?client=SOCIAL&type=FAVICON&fallback_opts=TYPE,SIZE,URL&url=${encodeURIComponent(siteUrl)}&size=32`;
|
// 根据 icons.region 配置决定优先使用哪个域名
|
||||||
const faviconUrlFallback = `https://t3.gstatic.cn/faviconV2?client=SOCIAL&type=FAVICON&fallback_opts=TYPE,SIZE,URL&url=${encodeURIComponent(siteUrl)}&size=32`;
|
const faviconUrlPrimary = iconsRegion === 'cn'
|
||||||
|
? `https://t3.gstatic.cn/faviconV2?client=SOCIAL&type=FAVICON&fallback_opts=TYPE,SIZE,URL&url=${encodeURIComponent(siteUrl)}&size=32`
|
||||||
|
: `https://t3.gstatic.com/faviconV2?client=SOCIAL&type=FAVICON&fallback_opts=TYPE,SIZE,URL&url=${encodeURIComponent(siteUrl)}&size=32`;
|
||||||
|
const faviconUrlFallback = iconsRegion === 'cn'
|
||||||
|
? `https://t3.gstatic.com/faviconV2?client=SOCIAL&type=FAVICON&fallback_opts=TYPE,SIZE,URL&url=${encodeURIComponent(siteUrl)}&size=32`
|
||||||
|
: `https://t3.gstatic.cn/faviconV2?client=SOCIAL&type=FAVICON&fallback_opts=TYPE,SIZE,URL&url=${encodeURIComponent(siteUrl)}&size=32`;
|
||||||
|
|
||||||
const iconContainer = document.createElement('div');
|
const iconContainer = document.createElement('div');
|
||||||
iconContainer.className = 'icon-container';
|
iconContainer.className = 'icon-container';
|
||||||
@@ -457,14 +477,38 @@ window.MeNav = {
|
|||||||
favicon.alt = `${siteName} favicon`;
|
favicon.alt = `${siteName} favicon`;
|
||||||
favicon.loading = 'lazy';
|
favicon.loading = 'lazy';
|
||||||
let faviconFallbackTried = false;
|
let faviconFallbackTried = false;
|
||||||
|
let loadTimeout = null;
|
||||||
|
|
||||||
|
// 超时处理:3秒后如果还没加载成功,尝试回退 URL 或显示 Font Awesome 图标
|
||||||
|
const startTimeout = () => {
|
||||||
|
if (loadTimeout) clearTimeout(loadTimeout);
|
||||||
|
loadTimeout = setTimeout(() => {
|
||||||
|
if (!favicon.classList.contains('loaded')) {
|
||||||
|
if (!faviconFallbackTried) {
|
||||||
|
faviconFallbackTried = true;
|
||||||
|
favicon.src = faviconUrlFallback;
|
||||||
|
startTimeout(); // 为 fallback URL 也设置超时
|
||||||
|
} else {
|
||||||
|
favicon.classList.add('error');
|
||||||
|
placeholder.classList.add('hidden');
|
||||||
|
fallback.classList.add('visible');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, 3000);
|
||||||
|
};
|
||||||
|
startTimeout();
|
||||||
|
|
||||||
favicon.addEventListener('load', () => {
|
favicon.addEventListener('load', () => {
|
||||||
|
if (loadTimeout) clearTimeout(loadTimeout);
|
||||||
favicon.classList.add('loaded');
|
favicon.classList.add('loaded');
|
||||||
placeholder.classList.add('hidden');
|
placeholder.classList.add('hidden');
|
||||||
});
|
});
|
||||||
favicon.addEventListener('error', () => {
|
favicon.addEventListener('error', () => {
|
||||||
|
if (loadTimeout) clearTimeout(loadTimeout);
|
||||||
if (!faviconFallbackTried) {
|
if (!faviconFallbackTried) {
|
||||||
faviconFallbackTried = true;
|
faviconFallbackTried = true;
|
||||||
favicon.src = faviconUrlFallback;
|
favicon.src = faviconUrlFallback;
|
||||||
|
startTimeout(); // 为 fallback URL 也设置超时
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -38,11 +38,11 @@
|
|||||||
<i class="fas fa-circle-notch fa-spin icon-placeholder" aria-hidden="true"></i>
|
<i class="fas fa-circle-notch fa-spin icon-placeholder" aria-hidden="true"></i>
|
||||||
<img
|
<img
|
||||||
class="favicon-icon"
|
class="favicon-icon"
|
||||||
src="https://t3.gstatic.com/faviconV2?client=SOCIAL&type=FAVICON&fallback_opts=TYPE,SIZE,URL&url={{encodeURIComponent url}}&size=32"
|
src="{{faviconUrl url}}"
|
||||||
alt="{{name}} favicon"
|
alt="{{name}} favicon"
|
||||||
loading="lazy"
|
loading="lazy"
|
||||||
onload="this.classList.add('loaded'); this.previousElementSibling.classList.add('hidden');"
|
onload="this.classList.add('loaded'); this.previousElementSibling.classList.add('hidden');"
|
||||||
onerror="if (!this.dataset.faviconFallbackTried) { this.dataset.faviconFallbackTried = '1'; var next = this.src.replace('t3.gstatic.com', 't3.gstatic.cn'); if (next !== this.src) { this.src = next; return; } } this.classList.add('error'); this.previousElementSibling.classList.add('hidden'); this.nextElementSibling.classList.add('visible');"
|
onerror="if (!this.dataset.faviconFallbackTried) { this.dataset.faviconFallbackTried = '1'; this.src = '{{faviconFallbackUrl url}}'; return; } this.classList.add('error'); this.previousElementSibling.classList.add('hidden'); this.nextElementSibling.classList.add('visible');"
|
||||||
/>
|
/>
|
||||||
<i class="{{#if icon}}{{icon}}{{else}}fas fa-link{{/if}} icon-fallback" aria-hidden="true"></i>
|
<i class="{{#if icon}}{{icon}}{{else}}fas fa-link{{/if}} icon-fallback" aria-hidden="true"></i>
|
||||||
</div>
|
</div>
|
||||||
@@ -56,11 +56,11 @@
|
|||||||
<i class="fas fa-circle-notch fa-spin icon-placeholder" aria-hidden="true"></i>
|
<i class="fas fa-circle-notch fa-spin icon-placeholder" aria-hidden="true"></i>
|
||||||
<img
|
<img
|
||||||
class="favicon-icon"
|
class="favicon-icon"
|
||||||
src="https://t3.gstatic.com/faviconV2?client=SOCIAL&type=FAVICON&fallback_opts=TYPE,SIZE,URL&url={{encodeURIComponent url}}&size=32"
|
src="{{faviconUrl url}}"
|
||||||
alt="{{name}} favicon"
|
alt="{{name}} favicon"
|
||||||
loading="lazy"
|
loading="lazy"
|
||||||
onload="this.classList.add('loaded'); this.previousElementSibling.classList.add('hidden');"
|
onload="this.classList.add('loaded'); this.previousElementSibling.classList.add('hidden');"
|
||||||
onerror="if (!this.dataset.faviconFallbackTried) { this.dataset.faviconFallbackTried = '1'; var next = this.src.replace('t3.gstatic.com', 't3.gstatic.cn'); if (next !== this.src) { this.src = next; return; } } this.classList.add('error'); this.previousElementSibling.classList.add('hidden'); this.nextElementSibling.classList.add('visible');"
|
onerror="if (!this.dataset.faviconFallbackTried) { this.dataset.faviconFallbackTried = '1'; this.src = '{{faviconFallbackUrl url}}'; return; } this.classList.add('error'); this.previousElementSibling.classList.add('hidden'); this.nextElementSibling.classList.add('visible');"
|
||||||
/>
|
/>
|
||||||
<i class="{{#if icon}}{{icon}}{{else}}fas fa-link{{/if}} icon-fallback" aria-hidden="true"></i>
|
<i class="{{#if icon}}{{icon}}{{else}}fas fa-link{{/if}} icon-fallback" aria-hidden="true"></i>
|
||||||
</div>
|
</div>
|
||||||
@@ -156,11 +156,11 @@
|
|||||||
<i class="fas fa-circle-notch fa-spin icon-placeholder" aria-hidden="true"></i>
|
<i class="fas fa-circle-notch fa-spin icon-placeholder" aria-hidden="true"></i>
|
||||||
<img
|
<img
|
||||||
class="favicon-icon"
|
class="favicon-icon"
|
||||||
src="https://t3.gstatic.com/faviconV2?client=SOCIAL&type=FAVICON&fallback_opts=TYPE,SIZE,URL&url={{encodeURIComponent url}}&size=32"
|
src="{{faviconUrl url}}"
|
||||||
alt="{{name}} favicon"
|
alt="{{name}} favicon"
|
||||||
loading="lazy"
|
loading="lazy"
|
||||||
onload="this.classList.add('loaded'); this.previousElementSibling.classList.add('hidden');"
|
onload="this.classList.add('loaded'); this.previousElementSibling.classList.add('hidden');"
|
||||||
onerror="if (!this.dataset.faviconFallbackTried) { this.dataset.faviconFallbackTried = '1'; var next = this.src.replace('t3.gstatic.com', 't3.gstatic.cn'); if (next !== this.src) { this.src = next; return; } } this.classList.add('error'); this.previousElementSibling.classList.add('hidden'); this.nextElementSibling.classList.add('visible');"
|
onerror="if (!this.dataset.faviconFallbackTried) { this.dataset.faviconFallbackTried = '1'; this.src = '{{faviconFallbackUrl url}}'; return; } this.classList.add('error'); this.previousElementSibling.classList.add('hidden'); this.nextElementSibling.classList.add('visible');"
|
||||||
/>
|
/>
|
||||||
<i class="{{#if icon}}{{icon}}{{else}}fas fa-link{{/if}} icon-fallback" aria-hidden="true"></i>
|
<i class="{{#if icon}}{{icon}}{{else}}fas fa-link{{/if}} icon-fallback" aria-hidden="true"></i>
|
||||||
</div>
|
</div>
|
||||||
@@ -174,11 +174,11 @@
|
|||||||
<i class="fas fa-circle-notch fa-spin icon-placeholder" aria-hidden="true"></i>
|
<i class="fas fa-circle-notch fa-spin icon-placeholder" aria-hidden="true"></i>
|
||||||
<img
|
<img
|
||||||
class="favicon-icon"
|
class="favicon-icon"
|
||||||
src="https://t3.gstatic.com/faviconV2?client=SOCIAL&type=FAVICON&fallback_opts=TYPE,SIZE,URL&url={{encodeURIComponent url}}&size=32"
|
src="{{faviconUrl url}}"
|
||||||
alt="{{name}} favicon"
|
alt="{{name}} favicon"
|
||||||
loading="lazy"
|
loading="lazy"
|
||||||
onload="this.classList.add('loaded'); this.previousElementSibling.classList.add('hidden');"
|
onload="this.classList.add('loaded'); this.previousElementSibling.classList.add('hidden');"
|
||||||
onerror="if (!this.dataset.faviconFallbackTried) { this.dataset.faviconFallbackTried = '1'; var next = this.src.replace('t3.gstatic.com', 't3.gstatic.cn'); if (next !== this.src) { this.src = next; return; } } this.classList.add('error'); this.previousElementSibling.classList.add('hidden'); this.nextElementSibling.classList.add('visible');"
|
onerror="if (!this.dataset.faviconFallbackTried) { this.dataset.faviconFallbackTried = '1'; this.src = '{{faviconFallbackUrl url}}'; return; } this.classList.add('error'); this.previousElementSibling.classList.add('hidden'); this.nextElementSibling.classList.add('visible');"
|
||||||
/>
|
/>
|
||||||
<i class="{{#if icon}}{{icon}}{{else}}fas fa-link{{/if}} icon-fallback" aria-hidden="true"></i>
|
<i class="{{#if icon}}{{icon}}{{else}}fas fa-link{{/if}} icon-fallback" aria-hidden="true"></i>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user