diff --git a/assets/javascripts/app.js b/assets/javascripts/app.js
index b16bf08..8fe60cf 100644
--- a/assets/javascripts/app.js
+++ b/assets/javascripts/app.js
@@ -902,7 +902,7 @@
var isLinkImage = $parentA.length > 0 && $parentA.text().replace(/\u00a0/g, '').trim() === "";
// --- 1. 處理「裝飾性圖片」標註 ---
- if (currentAlt === "announcement image" || currentAlt === "裝飾圖片" || currentTitle === "裝飾圖片") {
+ if (currentAlt === "announcement image" || currentAlt === "裝飾圖片" || currentTitle === "裝飾圖片" || currentAlt === "這是一張圖片" || currentTitle === "這是一張圖片") {
$img.attr('alt', '');
$img.removeAttr('title');
return;
@@ -1188,108 +1188,132 @@
}, 800);
});
- $(function() {
-function smartFixIcons() {
- $('i').each(function() {
- var $icon = $(this);
- var $parent = $icon.parent();
+$(function() {
+ function smartFixIcons() {
+ var isEn = $('html').attr('lang') === 'en';
- // 1. 所有圖示先對螢幕閱讀器隱藏
- $icon.attr('aria-hidden', 'true');
+ var labels = {
+ calendar : isEn ? 'Date: ' : '日期:',
+ user : isEn ? 'Author: ' : '發布者:',
+ tag : isEn ? 'Tag: ' : '標籤:',
+ clock : isEn ? 'Time: ' : '時間:',
+ location : isEn ? 'Location: ' : '地點:',
+ phone : isEn ? 'Phone: ' : '電話:',
+ envelope : isEn ? 'Email: ' : '信箱:',
+ folder : isEn ? 'Category: ' : '分類:',
+ eye : isEn ? 'Views: ' : '瀏覽次數:',
+ download : isEn ? 'Download: ' : '下載檔案:',
+ search : isEn ? 'Search' : '搜尋',
+ bars : isEn ? 'Menu' : '選單',
+ print : isEn ? 'Print this page' : '列印此頁',
+ share : isEn ? 'Share' : '分享至社群',
+ close : isEn ? 'Close window' : '關閉視窗',
+ closePopup : isEn ? 'Close announcement' : '關閉公告視窗',
+ closeGallery: isEn ? 'Close gallery' : '關閉相簿',
+ toggleDesc : isEn ? 'Show or hide image description' : '顯示或隱藏圖片說明',
+ themeSwitch : isEn ? 'Switch gallery theme': '切換相簿主題',
+ showOriginal: isEn ? 'Show original image' : '顯示原始圖片'
+ };
- // 特判:.close-screen-btn 內的圖示,移除無關的 sr-only 並設正確 aria-label
- if ($parent.hasClass('close-screen-btn')) {
- $parent.find('.sr-only').remove();
- $parent.attr('aria-label', '關閉公告視窗');
- if ($icon.next('.sr-only').length === 0) {
- $icon.after('關閉公告視窗');
+ $('i').each(function() {
+ var $icon = $(this);
+ var $parent = $icon.parent();
+
+ // 1. 所有圖示先對螢幕閱讀器隱藏
+ $icon.attr('aria-hidden', 'true');
+
+ // 特判:.close-screen-btn 內的圖示,移除無關的 sr-only 並設正確 aria-label
+ if ($parent.hasClass('close-screen-btn')) {
+ $parent.find('.sr-only').remove();
+ $parent.attr('aria-label', labels.closePopup);
+ if ($icon.next('.sr-only').length === 0) {
+ $icon.after('' + labels.closePopup + '');
+ }
+ return;
}
- return;
- }
- // 特判:.gallery-close 內的圖示,移除錯誤的 sr-only「時間:」並補正確說明
- if ($parent.closest('.gallery-close').length > 0) {
- $parent.closest('.gallery-close').find('.sr-only').remove();
- $parent.closest('.gallery-close').attr('aria-label', '關閉相簿');
- $parent.closest('.gallery-close').attr('role', 'button');
- $parent.closest('.gallery-close').attr('tabindex', '0');
- if ($icon.next('.sr-only').length === 0) {
- $icon.after('關閉相簿');
+ // 特判:.gallery-close 內的圖示,移除錯誤的 sr-only 並補正確說明
+ if ($parent.closest('.gallery-close').length > 0) {
+ $parent.closest('.gallery-close').find('.sr-only').remove();
+ $parent.closest('.gallery-close').attr('aria-label', labels.closeGallery);
+ $parent.closest('.gallery-close').attr('role', 'button');
+ $parent.closest('.gallery-close').attr('tabindex', '0');
+ if ($icon.next('.sr-only').length === 0) {
+ $icon.after('' + labels.closeGallery + '');
+ }
+ return;
}
- return;
- }
- // 特判:.gallery-toggle-desc 補說明
- if ($parent.closest('.gallery-toggle-desc').length > 0) {
- var $toggleBtn = $parent.closest('.gallery-toggle-desc');
- if (!$toggleBtn.attr('aria-label')) {
- $toggleBtn.attr('aria-label', '顯示或隱藏圖片說明');
- $toggleBtn.attr('role', 'button');
- $toggleBtn.attr('tabindex', '0');
+ // 特判:.gallery-toggle-desc 補說明
+ if ($parent.closest('.gallery-toggle-desc').length > 0) {
+ var $toggleBtn = $parent.closest('.gallery-toggle-desc');
+ if (!$toggleBtn.attr('aria-label')) {
+ $toggleBtn.attr('aria-label', labels.toggleDesc);
+ $toggleBtn.attr('role', 'button');
+ $toggleBtn.attr('tabindex', '0');
+ }
+ if ($icon.next('.sr-only').length === 0) {
+ $icon.after('' + labels.toggleDesc + '');
+ }
+ return;
}
- if ($icon.next('.sr-only').length === 0) {
- $icon.after('顯示或隱藏圖片說明');
+
+ // 特判:.gallery-theme-switch 補說明
+ if ($parent.closest('.gallery-theme-switch').length > 0) {
+ var $themeBtn = $parent.closest('.gallery-theme-switch');
+ if (!$themeBtn.attr('aria-label')) {
+ $themeBtn.attr('aria-label', labels.themeSwitch);
+ $themeBtn.attr('role', 'button');
+ $themeBtn.attr('tabindex', '0');
+ }
+ if ($icon.next('.sr-only').length === 0) {
+ $icon.after('' + labels.themeSwitch + '');
+ }
+ return;
}
- return;
- }
- // 特判:.gallery-theme-switch 補說明
- if ($parent.closest('.gallery-theme-switch').length > 0) {
- var $themeBtn = $parent.closest('.gallery-theme-switch');
- if (!$themeBtn.attr('aria-label')) {
- $themeBtn.attr('aria-label', '切換相簿主題');
- $themeBtn.attr('role', 'button');
- $themeBtn.attr('tabindex', '0');
+ // 2. 已有 aria-label 或 sr-only 就不重複處理
+ if ($icon.attr('aria-label') || $icon.next('.sr-only').length > 0 || $parent.find('.sr-only').length > 0) {
+ return;
}
- if ($icon.next('.sr-only').length === 0) {
- $icon.after('切換相簿主題');
+
+ var cls = ($icon.attr('class') || '').toLowerCase();
+ var labelText = '';
+
+ // --- 策略 A:常見資訊型圖示 ---
+ if (cls.includes('calendar')) labelText = labels.calendar;
+ else if (cls.includes('user') || cls.includes('male')) labelText = labels.user;
+ else if (cls.includes('tag')) labelText = labels.tag;
+ else if (cls.includes('clock') || cls.includes('time')) labelText = labels.clock;
+ else if (cls.includes('map-marker') || cls.includes('location')) labelText = labels.location;
+ else if (cls.includes('phone')) labelText = labels.phone;
+ else if (cls.includes('envelope') || cls.includes('mail')) labelText = labels.envelope;
+ else if (cls.includes('folder')) labelText = labels.folder;
+ else if (cls.includes('eye')) labelText = labels.eye;
+ else if (cls.includes('download')) labelText = labels.download;
+
+ // --- 策略 B:功能性按鈕 ---
+ var $container = $icon.closest('a, button');
+ if ($container.length > 0 && $container.text().trim() === '') {
+ var btnTitle = $container.attr('title');
+ if (btnTitle) labelText = btnTitle;
+ else if (cls.includes('search')) labelText = labels.search;
+ else if (cls.includes('bars') || cls.includes('navicon')) labelText = labels.bars;
+ else if (cls.includes('print')) labelText = labels.print;
+ else if (cls.includes('share')) labelText = labels.share;
+ else if (cls.includes('close') || cls.includes('times')) labelText = labels.close;
+ else if (cls.includes('image') || cls.includes('photo')) labelText = labels.showOriginal;
}
- return;
- }
- // 2. 已有 aria-label 或 sr-only 就不重複處理
- if ($icon.attr('aria-label') || $icon.next('.sr-only').length > 0 || $parent.find('.sr-only').length > 0) {
- return;
- }
-
- var cls = ($icon.attr('class') || '').toLowerCase();
- var labelText = '';
-
- // --- 策略 A:常見資訊型圖示 ---
- if (cls.includes('calendar')) labelText = '日期:';
- else if (cls.includes('user') || cls.includes('male')) labelText = '發布者:';
- else if (cls.includes('tag')) labelText = '標籤:';
- else if (cls.includes('clock') || cls.includes('time')) labelText = '時間:';
- else if (cls.includes('map-marker') || cls.includes('location')) labelText = '地點:';
- else if (cls.includes('phone')) labelText = '電話:';
- else if (cls.includes('envelope') || cls.includes('mail')) labelText = '信箱:';
- else if (cls.includes('folder')) labelText = '分類:';
- else if (cls.includes('eye')) labelText = '瀏覽次數:';
- else if (cls.includes('download')) labelText = '下載檔案:';
-
- // --- 策略 B:功能性按鈕 ---
- var $container = $icon.closest('a, button');
- if ($container.length > 0 && $container.text().trim() === '') {
- var btnTitle = $container.attr('title');
- if (btnTitle) labelText = btnTitle;
- else if (cls.includes('search')) labelText = '搜尋';
- else if (cls.includes('bars') || cls.includes('navicon')) labelText = '選單';
- else if (cls.includes('print')) labelText = '列印此頁';
- else if (cls.includes('share')) labelText = '分享至社群';
- else if (cls.includes('close') || cls.includes('times')) labelText = '關閉視窗';
- else if (cls.includes('image') || cls.includes('photo')) labelText = '顯示原始圖片';
- }
-
- // --- 3. 執行補強 ---
- if (labelText !== '') {
- $icon.after('' + labelText + '');
- }
- });
-}
-
- // 延遲執行確保內容渲染完成
- setTimeout(smartFixIcons, 500);
+ // --- 3. 執行補強 ---
+ if (labelText !== '') {
+ $icon.after('' + labelText + '');
+ }
});
+ }
+
+ setTimeout(smartFixIcons, 500);
+});
// 刪除banner-slide的空連結和空連結目標
for(var i=0;i<$('.w-ba-banner__slide a').length;i++){
if($('.w-ba-banner__slide a').eq(i).attr('href')=="")
@@ -1514,6 +1538,72 @@ function smartFixIcons() {
resizeTimer = setTimeout(fixAccessKeyM, 150);
});
});
+
+ //導覽選單子選單無障礙狀態修正 (支援多層級)
+ function fixNavMenuA11y() {
+ const $navItems = $('#main-nav li');
+
+ $navItems.each(function() {
+ const $li = $(this);
+ const $toggle = $li.children('a');
+ const $subMenu = $li.children('ul');
+
+ // 判斷是否有子選單
+ if ($subMenu.length > 0) {
+ // 1. 初始化屬性
+ // aria-haspopup="true" 表示有點擊後彈出選單的功能
+ // aria-expanded="false" 預設為摺疊
+ $toggle.attr({
+ 'aria-haspopup': 'true',
+ 'aria-expanded': 'false'
+ });
+
+ // 如果 href 是 javascript:void(0);,建議補上 role="button" 讓語音讀出它是按鈕
+ if ($toggle.attr('href').indexOf('javascript') !== -1) {
+ $toggle.attr('role', 'button');
+ }
+
+ // 2. 焦點移入事件 (Focus)
+ $toggle.on('focus', function() {
+ // 這裡假設你的選單是用 CSS 或其他 JS 控制顯示,
+ // 當焦點進入時,將狀態改為 true
+ $(this).attr('aria-expanded', 'true');
+ // 如果需要透過 JS 強制顯示選單可在此加上:$subMenu.addClass('show'); (視你的 CSS 而定)
+ });
+
+ // 3. 處理失焦事件 (Focusout)
+ // 使用 focusout 並檢查焦點是否移出了整個 li 區塊
+ $li.on('focusout', function(e) {
+ // 使用 setTimeout 確保能抓到下一個獲焦元素
+ setTimeout(function() {
+ if (!$li.is(':focus-within')) {
+ $toggle.attr('aria-expanded', 'false');
+ // 如果有 class 控制顯示:$subMenu.removeClass('show');
+ }
+ }, 50);
+ });
+ }
+ });
+ }
+ // 執行修正
+ fixNavMenuA11y();
+ //修正無超連結目的之連結
+ $(function() {
+ /**
+ * 針對所有 href="#" 的連結改為 javascript:void(0);
+ * 避免使用者點擊後頁面跳回頂端,造成操作困擾
+ */
+ function fixEmptyHref() {
+ $('a[href="#"]').each(function() {
+ // 將 href 修改為不觸發跳轉的 void 腳本
+ $(this).attr('href', 'javascript:void(0);');
+ $(this).attr('role', 'button');
+ });
+ }
+
+ // 延遲執行,確保動態產生的選單也能被補強
+ setTimeout(fixEmptyHref, 500);
+ });
//公告頁籤無障礙
$(function() {
function fixTabLinearOrder() {
@@ -1600,71 +1690,6 @@ function smartFixIcons() {
fixTabLinearOrder();
});
- //導覽選單子選單無障礙狀態修正 (支援多層級)
- function fixNavMenuA11y() {
- const $navItems = $('#main-nav li');
-
- $navItems.each(function() {
- const $li = $(this);
- const $toggle = $li.children('a');
- const $subMenu = $li.children('ul');
-
- // 判斷是否有子選單
- if ($subMenu.length > 0) {
- // 1. 初始化屬性
- // aria-haspopup="true" 表示有點擊後彈出選單的功能
- // aria-expanded="false" 預設為摺疊
- $toggle.attr({
- 'aria-haspopup': 'true',
- 'aria-expanded': 'false'
- });
-
- // 如果 href 是 javascript:void(0);,建議補上 role="button" 讓語音讀出它是按鈕
- if ($toggle.attr('href').indexOf('javascript') !== -1) {
- $toggle.attr('role', 'button');
- }
-
- // 2. 焦點移入事件 (Focus)
- $toggle.on('focus', function() {
- // 這裡假設你的選單是用 CSS 或其他 JS 控制顯示,
- // 當焦點進入時,將狀態改為 true
- $(this).attr('aria-expanded', 'true');
- // 如果需要透過 JS 強制顯示選單可在此加上:$subMenu.addClass('show'); (視你的 CSS 而定)
- });
-
- // 3. 處理失焦事件 (Focusout)
- // 使用 focusout 並檢查焦點是否移出了整個 li 區塊
- $li.on('focusout', function(e) {
- // 使用 setTimeout 確保能抓到下一個獲焦元素
- setTimeout(function() {
- if (!$li.is(':focus-within')) {
- $toggle.attr('aria-expanded', 'false');
- // 如果有 class 控制顯示:$subMenu.removeClass('show');
- }
- }, 50);
- });
- }
- });
- }
- // 執行修正
- fixNavMenuA11y();
- //修正無超連結目的之連結
- $(function() {
- /**
- * 針對所有 href="#" 的連結改為 javascript:void(0);
- * 避免使用者點擊後頁面跳回頂端,造成操作困擾
- */
- function fixEmptyHref() {
- $('a[href="#"]').each(function() {
- // 將 href 修改為不觸發跳轉的 void 腳本
- $(this).attr('href', 'javascript:void(0);');
- $(this).attr('role', 'button');
- });
- }
-
- // 延遲執行,確保動態產生的選單也能被補強
- setTimeout(fixEmptyHref, 500);
- });
//頁籤無障礙
$(function () {
function fixTabAccessibility() {
@@ -3011,11 +3036,130 @@ $(function() {
attributeFilter: ['style']
});
});
- // 執行 member等高計算,目前改用flexbox故mark掉 by ika 20160105
- // $(window).load(function() {
- // if ($('.index-member-3').length && $(window).width() > 992) {
- // ORBITFRONT.member.equalHeight('.i-member-item-inner');
- // }
- // });
+//萬用表格無障礙
+function fixSearchInputA11y() {
+ $('input[type="search"], input[type="text"]').each(function () {
+ var $input = $(this);
+
+ // 已有對應 label 就跳過
+ var inputId = $input.attr('id');
+ if (inputId && $('label[for="' + inputId + '"]').length) return;
+
+ // 已有 aria-label / aria-labelledby / title 就跳過
+ if ($input.attr('aria-label') || $input.attr('aria-labelledby') || $input.attr('title')) return;
+
+ // 從 placeholder 取得說明文字
+ var placeholder = $input.attr('placeholder') || '';
+
+ // 從最近的 th 或欄位標題取得欄位名稱
+ var colName = '';
+ var $th = $input.closest('th, div[class*="col-"]');
+ if ($th.length) {
+ colName = $th.find('.universal-th-text').text().trim();
+ }
+
+ // 組合標籤文字
+ var label = '';
+ if (colName && placeholder) {
+ label = colName + '搜尋';
+ } else if (colName) {
+ label = colName + '搜尋';
+ } else if (placeholder) {
+ label = placeholder;
+ } else {
+ label = '搜尋關鍵字';
+ }
+
+ $input.attr('aria-label', label);
+ $input.attr('title', label);
+ });
+}
+
+$(document).ready(function () {
+ setTimeout(fixSearchInputA11y, 500);
+});
+//萬用表格無障礙
+function fixSortLinkA11y() {
+ $('a').filter(function () {
+ return $(this).find('.universal-th-icon').length > 0;
+ }).each(function () {
+ var $link = $(this);
+
+ // === Step 1: 移除 display:none 的 span ===
+ $link.find('span').each(function () {
+ if ($(this).css('display') === 'none' || $(this).attr('style') && $(this).attr('style').indexOf('display: none') !== -1) {
+ $(this).remove();
+ }
+ });
+
+ // === Step 2: 取得 aria-label ===
+ var ariaLabel = $link.attr('aria-label') || '';
+
+ // 若 aria-label 空白或含未替換模板變數,從旁邊 .universal-th-text 補
+ if (!ariaLabel || ariaLabel.indexOf('{{') !== -1) {
+ var $th = $link.closest('th, div[class*="col-"]');
+ var colName = $th.find('.universal-th-text').text().trim();
+ ariaLabel = colName ? '重新排序' + colName : '排序';
+ $link.attr('aria-label', ariaLabel);
+ }
+
+ // === Step 3: 補上 title 屬性 ===
+ if (!$link.attr('title')) {
+ $link.attr('title', ariaLabel + '(在本視窗開啟)');
+ }
+
+ // === Step 4: 確保圖示有 aria-hidden ===
+ $link.find('.universal-th-icon').attr('aria-hidden', 'true');
+
+ // === Step 5: 補上 visually-hidden 連結文字 ===
+ if ($link.find('.visually-hidden').length === 0) {
+ $link.append('' + ariaLabel + '');
+ }
+ });
+}
+function fixSearchInputA11y() {
+ $('input[type="search"], input[type="text"]').each(function () {
+ var $input = $(this);
+
+ // 已有對應 label 就跳過
+ var inputId = $input.attr('id');
+ if (inputId && $('label[for="' + inputId + '"]').length) return;
+
+ // 已有 aria-label / aria-labelledby / title 就跳過
+ if ($input.attr('aria-label') || $input.attr('aria-labelledby') || $input.attr('title')) return;
+
+ // 從 placeholder 取得說明文字
+ var placeholder = $input.attr('placeholder') || '';
+
+ // 從最近的 th 或欄位標題取得欄位名稱
+ var colName = '';
+ var $th = $input.closest('th, div[class*="col-"]');
+ if ($th.length) {
+ colName = $th.find('.universal-th-text').text().trim();
+ }
+
+ // 組合標籤文字
+ var label = '';
+ if (colName && placeholder) {
+ label = colName + '搜尋';
+ } else if (colName) {
+ label = colName + '搜尋';
+ } else if (placeholder) {
+ label = placeholder;
+ } else {
+ label = '搜尋關鍵字';
+ }
+
+ $input.attr('aria-label', label);
+ $input.attr('title', label);
+ });
+}
+
+$(document).ready(function () {
+ setTimeout(function () {
+ fixSortLinkA11y();
+ fixSearchInputA11y();
+ }, 500);
+});
}(jQuery, window));
diff --git a/assets/stylesheets/template/modules/announcement.scss b/assets/stylesheets/template/modules/announcement.scss
index 235a9f9..037250c 100644
--- a/assets/stylesheets/template/modules/announcement.scss
+++ b/assets/stylesheets/template/modules/announcement.scss
@@ -2516,8 +2516,9 @@ table.dataTable.dtr-inline.collapsed>tbody>tr.parent>td:first-child:before, tabl
border:0;
&:hover{
.i-annc__content-wrap{
- background: radial-gradient(circle at 70% 80%, #66a9e2b3, transparent 60%), radial-gradient(circle at 1% 1%, #fb8f72, transparent 30%), radial-gradient(circle at 50% 50%, #3c78ff4d, transparent 70%), linear-gradient(135deg, #2b7bbe 0%, #00437c 80%, #fb8f72 100%);
+ background: radial-gradient(circle at 70% 80%, #176bb2b3, transparent 60%), radial-gradient(circle at 1% 1%, #b54c30, transparent 30%), radial-gradient(circle at 50% 50%, rgba(60, 120, 255, 0.301961), transparent 70%), linear-gradient(135deg, #045ea9 0%, #00437c 80%, #cb3f19 100%);
color: #fff!important;
+ text-shadow: 0px 0px 8px #000000;
i,a,.i-annc__postdate-wrap,.i-annc__category-wrap,.i-annc__title{
color: #fff!important;
}