雲客Drupal源碼分析之前端JavaScript(四)防抖與消息處理

本主題接着《前端JavaScript(三)》講述,推薦按序號閱讀

核心庫core/ drupal.debounce

文件: /core/misc/debounce.es6.js

爲全局對象Drupal添加了一個方法:

  Drupal.debounce(func, wait, immediate)

該方法稱爲防抖函數,用於在給定毫秒時間段內最多執行一次給定的函數,參數如下:

func:給定的要執行的函數

wait:時間範圍,整數,單位毫秒,在該範圍內參數一最多被執行一次

immediate:前文講到在給定時間段範圍內最多執行一次,那麼這一次是在這個範圍的開始執行還是結束時執行呢?這就是該參數的意義,是一個布爾值,true表示在開始執行,反之在結尾執行,默認爲false

返回值是一個匿名函數,包裝了傳入的函數,稱爲傳入函數的防抖變體

示例如下:

    function f() {
        function fn() {
            console.log(Math.random());
        }
        let yunke = Drupal.debounce(fn, 5000, true);
        yunke();
        yunke();
        yunke();
 }

該示例中,在執行函數f時,裏面雖然三次調用yunke();但函數fn僅被執行一次(不管多少次調用,在5秒內僅執行一次)

在頻繁重複調用防抖變體時,如果參數immediate被設置爲true,那麼可以肯定在等待時間內回調一定會被執行一次,且僅一次(在本輪事件循環中執行),如果設置爲false,那麼只能講在等待時間內最多被執行一次,可能不被執行,因爲是以異步方式執行,每次調用都會推後異步回調的執行時間,示例如下:

    function f() {
        function fn() {
            console.log(Math.random());
        }
        let startTime = new Date();
        let loop = true;
        let yunke = Drupal.debounce(fn, 500, false);
        while (loop) {
            let d = (new Date()) - startTime;
            if (d > 5000) {
                loop = false;
            }
            yunke();
        }
    }

在該列中,執行f()時,回調fn被設置爲500毫秒內最多僅執行一次,在while循環中頻繁重複調用防抖變體yunke(),循環5秒,執行結果並不是fn被調用了10次,而是5.5秒後被執行一次。

該函數原理上很精煉,用到了閉包、異步編程等;用處有很多,尤其是高頻事件觸發回調時,比如鼠標移動、窗體大小調整等,能夠防止劇烈回調執行,這也是其稱爲防抖函數的原因。

注意:高頻調用Drupal.debounce(fn, 200)();是起不到防抖作用的,僅僅起到延遲執行的效果,因爲每次調用返回的都是不同的防抖變體。

 

核心庫core/drupal.announce

該庫用於支持輔助技術(通常爲殘障輔助設備),在理解該庫前需要先理解輔助技術相關知識:

有些人羣訪問web需要藉助輔助設備,比如失明和弱視、耳聾和聽力喪失、運動受限、言語障礙、光敏性或多種殘疾組合的殘疾人,以及身體功能退化的老人,他們使用屏幕閱讀器、盲文系統等等,這些可以是硬件也可以是軟件,這裏統稱爲輔助技術Assistive Technologies,爲了和這些輔助技術交互,W3C發起了一個Web無障礙計劃Web Accessibility Initiative,縮寫爲WAI,官網地址爲:

  https://www.w3.org/WAI/

Web內容無障礙中文指南 (WCAG) 2.1官方地址:

  https://www.w3.org/Translations/WCAG21-zh/

該計劃制定了一個ARIA規範用於具體實現,ARIA是“Accessible Rich Internet Applications”的縮寫,即可訪問的富互聯網應用程序規範,web開發者在開發無障礙訪問網頁時需要遵循該規範,規範官方地址爲:

  https://www.w3.org/TR/wai-aria-1.1/

該規範爲html標籤提供三種指示語義的屬性以支持輔助技術:角色、屬性、狀態,解釋如下:

role:

角色,表明一些html標籤是做什麼用的,如滑塊、菜單欄、對話框、選項卡等,示例:

<p id="ajax_error_alert" role="alert"></p>

該例告訴輔助技術這個p元素的身份是一個包含警告信息的角色,輔助技術應據此向用戶發出警告信息;規範指出:角色屬性一旦設定就不能被改變,需更改應刪除舊元素後建立新元素,角色全部可選值參見這裏:

   https://www.w3.org/TR/wai-aria-1.1/#role_definitions

aria-屬性名:

屬性,表明元素的特性,如是否可拖拽、是否必填等,不同屬性名及其不同值含義不同:

示例一:

<ul aria-busy="true" >

該例中aria-busy屬性表示當前區域是否正在更新:可選爲true, 表示該區域正在更新,如加載或修改,默認爲false, 表示與true相對的狀態,靜置中,或爲error,表示該區域出現錯誤。如果某個區域內(如這裏的ul)有多個地方需要修改,需要全部修改完畢再通知使用者的話,就可以先將aria-busy設爲true, 等到全部內容更新完畢後再設成false,該屬性可以避免輔助技術在區域內容更新完畢前不斷即時提醒使用者。

示例二:

<div aria-live="assertive" aria-busy="false"></div>

該例中aria-live指示通知用戶的行爲,其值是字符串,可選有:off、polite、assertive、rude。默認爲off, 表示不通知更新;polite表示只有用戶閒時通知;assertive表示儘快通知用戶;rude表示即時通知用戶,必要的時候甚至中斷用戶,絕大多數的更新應該在用戶閒暇的時候通知,即時提示對用戶是一種干擾,如果希望內容完全更新後再提示,可以使用上面提到的aria-busy

aria-狀態名:

狀態,表示當前交互的狀態,如是否選中、隱藏、禁用、展開、鍵盤按下等,示例如下:

  <li role="checkbox" aria-checked="true">全部產品</li>

全部狀態和屬性請見:

  https://www.w3.org/TR/wai-aria-1.1/#state_prop_def

元素的以上三種屬性這裏統稱爲無障礙屬性,其值均應小寫,有些元素默認具備一些無障礙屬性,詳見:

   https://www.w3.org/TR/html-aria/#dfn-implicit-aria-semantics

在有默認無障礙屬性的情況下無需明確指定,無障礙計劃誕生於HTML4之後,HTML5之前,很多H5標籤天然具備語義性,但在使用如div、span之類的無語義元素時,爲了實現無障礙訪問應當明確指定無障礙屬性;

現代瀏覽器在打開一個網頁時,除建立DOM樹外,還會建立可訪問樹(Accessibility Tree),這顆樹被映射給操作系統,後者提供給輔助技術,樹中的節點稱爲可訪問對象,它們由DOM對象和無障礙屬性信息綜合計算而來

庫解釋:

文件:core/misc/announce.es6.js

在文檔就緒後,該庫在頁面末尾追加一個專門用於通知輔助技術(如屏幕閱讀器)的元素:

<div id="drupal-live-announce" class="visually-hidden" aria-live="polite" aria-busy="false"></div>

該元素的內容發生變化時將會通知輔助技術,其他js可以通過以下方法向該元素添加內容:

  Drupal.announce(text, priority);

參數:text是要通知的文本內容,字符串值,如需翻譯應該是翻譯之後的

參數:priority表示緊急程度,字符串值,polite和assertive二選一,如果傳遞其他參數將默認爲polite,這也是默認值,表示在用戶閒暇時進行通知,assertive表示儘管通知用戶,有必要時中斷用戶進行通知

 

bug提示:該方法在實現上錯誤使用了防抖方法,修復如下:

  let announce_debounce = debounce(announce, 200);
  Drupal.announce = function (text, priority) {
    announcements.push({
      text,
      priority,
    });
    return announce_debounce();
  };

雲客已向官方報告,在發佈前,可自行修復

 

核心庫core/drupal.message

文件:/core/misc/message.es6.js

用於提供一個API方便其他JS在頁面上顯示消息,消息分三種類型:狀態status、警告warning、錯誤error(注意消息不是日誌,日誌有更多類型),詳見後端服務:\Drupal::messenger()。

該庫使用示例如下:

function yunke() {
  let message = new Drupal.Message();
  let id = message.add('js動態添加的狀態消息');
  setTimeout(function () {
    message.remove(id);
  }, 5000);
  //console.log(hasXor);
}

這將在頁面默認消息塊放置的地方顯示一條狀態消息,然後5秒後自動刪除

默認消息顯示區:

在系統開啓消息塊的情況下,會默認提供一個消息顯示區,消息塊由系統模塊提供,塊插件類:

  \Drupal\system\Plugin\Block\SystemMessagesBlock

該塊用到了狀態消息類型的渲染元素,元素類型插件類如下:

  \Drupal\Core\Render\Element\StatusMessages

消息塊提供了一個具備“data-drupal-messages”屬性的div元素來存放後端提供的消息,如果後端沒有消息將不提供該元素,相反只要消息塊被啓用,將始終提供一個具備屬性“data-drupal-messages-fallback”的div元素,以供前端顯示消息,顯示消息的標籤結構如下:

<div data-drupal-messages>
    <div class="messages__wrapper layout-container">
        <div role="contentinfo" aria-label="狀態消息" class="messages messages--status">
            <h2 class="visually-hidden">狀態消息</h2>
            <ul class="messages__list">
                <li class="messages__item">狀態消息1</li>
                <li class="messages__item">狀態消息2</li>
            </ul>
        </div>
        <div role="contentinfo" aria-label="警告信息" class="messages messages--warning">
            <h2 class="visually-hidden">警告信息</h2>
            警告消息1
        </div>
        <div role="contentinfo" aria-label="錯誤信息" class="messages messages--error">
            <div role="alert">
                <h2 class="visually-hidden">錯誤信息</h2>
                <ul class="messages__list">
                    <li class="messages__item">錯誤消息1</li>
                    <li class="messages__item">錯誤消息2</li>
                </ul>
            </div>
        </div>
    </div>
</div>

默認主題中該結構在以下模板中輸出:

  core/themes/bartik/templates/status-messages.html.twig

  core/themes/classy/templates/misc/status-messages.html.twig

在前端將尋找該元素,並用其第一個子元素作爲默認消息顯示區(也就是類爲“messages__wrapper”的元素),如果後端沒有提供消息,那麼該元素是不存在的,將退而尋找具備屬性“data-drupal-messages-fallback”的div元素(在消息塊啓用時該元素一定存在),然後爲其建立一個具備“messages__wrapper”類的子元素,並採用該子元素做消息顯示區。

自定義消息顯示區:

僅在沒有指定消息顯示區的情況下才採用前一節所述的默認消息顯示區,那麼如何採用自定義顯示區呢?

示例如下:

  let element=document.getElementById('sidebar-first');
  let message = new Drupal.Message(element);

如你所見,將指定的消息顯示區作爲構造函數的參數傳入即可

消息元素:

由主題方法“Drupal.theme.message”建立返回,是一個div元素,有如下屬性:

class:類,值有messages、messages--${type}

role:值爲:alert或status,依據類型而定

data-drupal-message-id:消息id

data-drupal-message-type:消息類型

aria-label:無障礙屬性,消息類型的名稱,供屏幕閱讀器設備使用

設計師可通過這些屬性來控制消息元素的外觀

 

該庫接口方法解釋如下:

add(message, options = {}):

向頁面添加顯示一條消息,參數message是一個字符串,表示要顯示的消息,其中可以帶html標籤,參數options是消息的上下文參數,有四個可用的屬性:

options.type:消息的類型,status、warning、error之一,如省略默認爲status,傳入其他值將拋出異常

options.id:這條消息的id,字符串值,如省略將隨機生成一個,格式爲“類型-15位隨機數字”,如:“status-553531552765107”

options.priority:用於通知輔助技術時,表示消息的緊急程度,字符串值,polite和assertive二選一,如果傳遞其他參數將默認爲polite,表示在用戶閒暇時進行通知,assertive表示儘管通知用戶,有必要時中斷用戶進行通知,通常我們不需要設置該參數,會依據消息類型自動判斷,當warning、error時自動設置爲assertive,一旦用戶設置,將不會進行自動判斷操作

options.announce:默認通知輔助技術的消息,如果全等於空字符串“''”將禁用通知,否則以該參數作爲通知的消息(應爲字符串值),如果該參數不存在,將以messsage參數作爲通知消息

該方法返回消息id,其他方法可依據返回的id去操作消息元素

 

select(id):

依據消息id返回消息元素的dom對象,僅在消息顯示區中查找

 

remove(id):

依據消息id移除消息元素,僅在消息顯示區中查找

 

clear():

移除消息顯示區中的全部消息

 

static announce(message, options):

將消息通知給輔助技術

 

messageInternalWrapper(messageWrapper):

傳遞一個元素,在其內頭部建立一個消息顯示區(具備類屬性“messages__wrapper”的div元素 ),並返回顯示區的dom對象

 

我是雲客,【雲遊天下,做客四方】,聯繫方式見主頁,歡迎轉載,但須註明出處

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章