# # ARIA

本章节主要包含如何快速运用 UIExtension 框架的内置功能实现可访问性，以及相关工具的介绍。如果你想详细了解 ARIA 的技术规范以及最佳实践，可以查阅以下的网址：

- https://www.w3.org/WAI/standards-guidelines/aria/
  (opens new window)
- https://www.w3.org/TR/wai-aria-practices-1.1/
  (opens new window)
- https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA
  (opens new window)

## # 如何使用 UIExtension 启用可访问性

初始化 PDFUI 时，我们需要加载 `aria` 附加组件:

    new UIExtension.PDFUI({
        addons: [
            // .... other Add-on
            'path/to/lib/uix-addons/aria/addon.info.json' // Make sure that aria is loaded last.
        ]
        // ... other options
    })

如果你的应用程序是通过 allInOne.js 来加载插件，因为它已经包含 `aria`，所以不需要额外单独加载：

    new UIExtension.PDFUI({
        addons: 'path/to/lib/uix-addons/allInOne.js'
        // ... other options
    })

关于如何加载 add-on 的更详细信息，请参阅 Addons。

## # UIExtension 内置组件

### # `aria-label` 属性

这些内置组件在指定 `text` 或 `label` 参数后会自动向生成的 DOM 节点上添加 `aria-label` 属性，可点击 `run` 按钮运行示例， 然后使用屏幕阅读器查看效果：

    <html>
        <template id="layout-template">
            <webpdf>
                <div>
                    <div>
                        <gtab text="XButton" group="aria-tab" body="tab1-body" active></gtab>
                        <gtab text="Dropdown" group="aria-tab" body="tab2-body"></gtab>
                        <gtab text="Slider" group="aria-tab" body="tab3-body"></gtab>
                        <gtab text="Ribbon button" group="aria-tab" body="tab4-body"></gtab>
                    </div>
                    <div name="tab1-body">
                        <xbutton text="button"></xbutton>
                    </div>
                    <div name="tab2-body">
                        <dropdown text="Dropdown" style="width: 8em" separate="false">
                            <xbutton text="Dropdown item 1"></xbutton>
                            <xbutton text="Dropdown item 2"></xbutton>
                        </dropdown>
                    </div>
                    <div name="tab3-body">
                        <slider min="0" max="100" step="1" label="Slider label"></slider>
                    </div>
                    <div name="tab4-body">
                        <ribbon-button text="Ribbon button"></ribbon-button>
                    </div>
                </div>
                <div class="fv__ui-body">
                    <viewer></viewer>
                </div>
            </webpdf>
        </template>
    </html>
    <script>
        var CustomAppearance = UIExtension.appearances.Appearance.extend({
            getLayoutTemplate: function() {
                return document.getElementById('layout-template').innerHTML;
            },
            disableAll: function(){}
        });
        var libPath = window.top.location.origin + '/lib';
        var pdfui = new UIExtension.PDFUI({
            viewerOptions: {
                libPath: libPath,
                jr: {
                    licenseSN: licenseSN,
                    licenseKey: licenseKey
                }
            },
            renderTo: document.body,
            appearance: CustomAppearance,
            addons: libPath + '/uix-addons/allInOne.js'
        });
    </script>

    {
        "iframeOptions": {
            "style": "height: 300px"
        }
    }

## # 视觉隐藏的内容

在有些场景下，我们需要提供一些可供屏幕阅读器等辅助技术访问但视觉上隐藏的内容，并且通过使用 `.fv__ui-aria-sr-only` 类进行样式设置。这样可以为视觉障碍用户提供有用的信息或提示，比如使用不同的颜色表示危险、警告等不同类型的信息，这种情况下就需要添加额外文本内容告知视觉障碍用户这条信息的类型。

    <p class="text-danger">
        <span class="fv__ui-aria-sr-only">Danger:</span>
        This action is not reversible.
    </p>

## # aria 指令

### # `@aria:attr` 指令

该指令用于设置以 `aria-` 开头的属性，指令的写法是 `@aria:attr.${aria-property-name}`，`aria-property-name` 可以参考 该网页
(opens new window)，其参数值是一个可执行的表达式：

    <html>
        <template id="layout-template">
            <webpdf>
                <div @var.value="50">
                    <input @aria:attr.valuemax="1" @aria:attr.valuemin="0" @aria:attr.valuenow="value + '%'">
                    <!--Result:
                        <input aria-valuemax="1" aria-valuemin="0" aria-valuenow="50%" ...>
                    -->

                    <!-- source text -->
                    <input @aria:attr.label="'source text'">
                    <!--Result:
                        <input aria-label="source text" ...>
                    -->

                    <!-- I18n key -->
                    <input @aria:attr.label="'aria:labels.gotopage'|i18n">
                    <!--Result:
                        <input aria-label="Set page" ...>
                    -->
                </div>
                <div class="fv__ui-body">
                    <viewer></viewer>
                </div>
            </webpdf>
        </template>
    </html>
    <script>
        var CustomAppearance = UIExtension.appearances.Appearance.extend({
            getLayoutTemplate: function() {
                return document.getElementById('layout-template').innerHTML;
            },
            disableAll: function(){}
        });
        var libPath = window.top.location.origin + '/lib';
        var pdfui = new UIExtension.PDFUI({
            viewerOptions: {
                libPath: libPath,
                jr: {
                    licenseSN: licenseSN,
                    licenseKey: licenseKey
                }
            },
            renderTo: document.body,
            appearance: CustomAppearance,
            addons: libPath + '/uix-addons/allInOne.js'
        });
    </script>

    {
        "iframeOptions": {
            "style": "height: 300px"
        }
    }

### # `@aria:label` 指令

该指令用于设置 `aria-label`
(opens new window) 属性， 指令值为 i18n 词条。

    <html>
        <template id="layout-template">
            <webpdf>
                <div>
                    <group-list>
                        <group>
                            <input @aria:label="aria:labels.gotopage" placeholder="@aria:label=&quot;aria:labels.gotopage&quot;">
                            <!--Result:
                                <input aria-label="Set page" ...>
                            -->

                            <!-- It is equivalent to: -->
                            <input @aria:attr.label="'aria:labels.gotopage'|i18n" placeholder="@aria:attr.label=&quot;'aria:labels.gotopage'|i18n&quot;">
                            <!--Result:
                                <input aria-label="Set page" ...>
                            -->

                            <!-- source text -->
                            <input @aria:label="source text" placeholder="@aria:label=&quot;source text&quot;">
                            <!--Result:
                                <input aria-label="source text" ...>
                            -->
                            <!-- It is equivalent to: -->
                            <input @aria:attr.label="'source text'" placeholder="@aria:attr.label=&quot;'source text'&quot;">
                            <!--Result:
                                <input aria-label="source text" ...>
                            -->
                        </group>
                    </group-list>
                    <group-list>
                        <group>
                            <!-- It can set attributes at a location specified by the dropdown component -->
                            <!-- Set aria-label on the drop-down arrow -->
                            <dropdown @aria:label.caret="Toggle Dropdown" text="@aria:label.caret"></dropdown>
                        </group>
                        <group>
                            <!-- Set aria-label on icons -->
                            <dropdown icon-class="fv__icon-toolbar-hand" @aria:label.icon="Click dropdown icon" text="@aria:label.icon"></dropdown>
                        </group>
                        <group>
                            <!-- Set aria-label on the input box -->
                            <dropdown
                                editable
                                @aria:label.editor="Click dropdown icon"
                                selected="1"
                                text="@aria:label.editor"
                            >
                                <dropdown-item>Item 2</dropdown-item>
                                <dropdown-item>Item 3</dropdown-item>
                                <dropdown-item>Item 1</dropdown-item>
                            </dropdown>
                        </group>
                        <group>
                            <!-- Set the aria-label of the list -->
                            <dropdown @aria:label.list="Items" text="@aria:label.list" separate="false">
                                <dropdown-item>Item 1</dropdown-item>
                                <dropdown-item>Item 2</dropdown-item>
                                <dropdown-item>Item 3</dropdown-item>
                            </dropdown>
                        </group>
                    </group-list>
                </div>
                <div class="fv__ui-body">
                    <viewer></viewer>
                </div>
            </webpdf>
        </template>
    </html>
    <script>
        var CustomAppearance = UIExtension.appearances.Appearance.extend({
            getLayoutTemplate: function() {
                return document.getElementById('layout-template').innerHTML;
            },
            disableAll: function(){}
        });
        var libPath = window.top.location.origin + '/lib';
        var pdfui = new UIExtension.PDFUI({
            viewerOptions: {
                libPath: libPath,
                jr: {
                    licenseSN: licenseSN,
                    licenseKey: licenseKey
                }
            },
            renderTo: document.body,
            appearance: CustomAppearance,
            addons: libPath + '/uix-addons/allInOne.js'
        });
    </script>
    <style>
        input {
            width: 20em;
        }
        .fv__ui-dropdown {
            width: auto;
        }
    </style>

    {
        "iframeOptions": {
            "style": "height: 300px"
        }
    }

### # `@aria:labelledby` 指令

`@aria:labelledby` 指令用于生成 `aria-labelledby` 属性。指令值使用的是 UIExtension 的选择器语法，而不是元素的 id。这是因为 `aria-labelledby` 属性需要通过 id 关联到另外一个元素，而 id 可能引发全局冲突问题，因此我们不建议手写 id 来索引元素。该指令的选择器语法可以避免这一问题。

有关 `aria-labelledby` 属性的更详细信息，请查阅 该网址
(opens new window)。

    <html>
        <template id="layout-template">
            <webpdf>
                <div>
                    <span>Field</span>
                    <!-- This is just for demonstration -->
                    <span name="a-colon">:</span>
                    <input type="text" @aria:labelledby="::parent()::childAt(0),a-colon">
                </div>
                <div class="fv__ui-body">
                    <viewer></viewer>
                </div>
            </webpdf>
        </template>
    </html>
    <script>
        var CustomAppearance = UIExtension.appearances.Appearance.extend({
            getLayoutTemplate: function() {
                return document.getElementById('layout-template').innerHTML;
            },
            disableAll: function(){}
        });
        var libPath = window.top.location.origin + '/lib';
        var pdfui = new UIExtension.PDFUI({
            viewerOptions: {
                libPath: libPath,
                jr: {
                    licenseSN: licenseSN,
                    licenseKey: licenseKey
                }
            },
            renderTo: document.body,
            appearance: CustomAppearance,
            addons: libPath + '/uix-addons/allInOne.js'
        });
    </script>
    <style>
        input {
            width: 20em;
        }
        .fv__ui-dropdown {
            width: auto;
        }
    </style>

    {
        "iframeOptions": {
            "style": "height: 300px"
        }
    }

上述示例中，`@aria:labelledby="::parent()::childAt(0),,a-colon"` 指定了 `<input>` 前面两个 span 组件的 id 为标签。有关选择器语法更详细的信息，可以参阅 组件选择器。

### # `@aria:describedby` 指令

`@aria:describedby` 指令的用法和原理和 `@aria:labelledby` 基本相同，此处不做过多说明。

有关 `aria-describedby` 更详细的信息，请查阅 该网址
(opens new window)。

    <html>
        <template id="layout-template">
            <webpdf>
                <div>
                    <span>Field:</span>
                    <input type="text" @aria:labelledby="::parent()::childAt(0)" @aria:describedby="::nextSiblings(),more-description">
                    <span class="fv__ui-aria-sr-only">
                        Description of text input.
                    </span>
                    <span class="fv__ui-aria-sr-only" name="more-description">
                        More description of text input.
                    </span>
                </div>
                <div class="fv__ui-body">
                    <viewer></viewer>
                </div>
            </webpdf>
        </template>
    </html>
    <script>
        var CustomAppearance = UIExtension.appearances.Appearance.extend({
            getLayoutTemplate: function() {
                return document.getElementById('layout-template').innerHTML;
            },
            disableAll: function(){}
        });
        var libPath = window.top.location.origin + '/lib';
        var pdfui = new UIExtension.PDFUI({
            viewerOptions: {
                libPath: libPath,
                jr: {
                    licenseSN: licenseSN,
                    licenseKey: licenseKey
                }
            },
            renderTo: document.body,
            appearance: CustomAppearance,
            addons: libPath + '/uix-addons/allInOne.js'
        });
    </script>
    <style>
        input {
            width: 20em;
        }
        .fv__ui-dropdown {
            width: auto;
        }
    </style>

    {
        "iframeOptions": {
            "style": "height: 300px"
        }
    }

### # `@aria:rel` 指令

ARIA 规范中，除了 `aria-labelledby` 和 `aria-describedby` 以外，还有很多需要关联其它元素的属性。`@aria:rel` 指令封装了这些常用的属性:

- `aria-activedescendant`
  (opens new window)，指令写法：`aria:rel.activedescendant="selecor,..."`
- `aria-flowto`
  (opens new window)，指令写法：`aria:rel.flowto="selecor,..."`
- `aria-owns`
  (opens new window)，指令写法：`aria:rel.owns="selecor,..."`
- `aria-controls`
  (opens new window)，指令写法：`aria:rel.controls="selecor,..."`

### # `@aria:circular-focus` 指令

该指令用来控制组件内元素的焦点，实现循环跳转功能。通常用于弹窗或者一个独立的 UI 区域。

可参照下面的示例，点击 `run` 按钮运行。

    <html>
        <template id="layout-template">
            <webpdf>
                <div>
                    <group-list>
                        <group>
                            <xbutton>button 1</xbutton>
                            <xbutton>button 2</xbutton>
                            <xbutton>button 3</xbutton>
                        </group>
                        <group @aria:circular-focus>
                            <xbutton @tooltip tooltip-title="Press 'Shift + Tab' will switch focus to 'button 6' instead of 'button 3'">button 4</xbutton>
                            <xbutton>button 5</xbutton>
                            <xbutton @tooltip tooltip-title="Press 'Tab' will switch focus to 'button 4' instead of 'button 7'">button 6</xbutton>
                        </group>
                        <group>
                            <xbutton>button 7</xbutton>
                        </group>
                    </group-list>
                </div>
                <div class="fv__ui-body">
                    <viewer></viewer>
                </div>
            </webpdf>
        </template>
    </html>
    <script>
        var CustomAppearance = UIExtension.appearances.Appearance.extend({
            getLayoutTemplate: function() {
                return document.getElementById('layout-template').innerHTML;
            },
            disableAll: function(){}
        });
        var libPath = window.top.location.origin + '/lib';
        var pdfui = new UIExtension.PDFUI({
            viewerOptions: {
                libPath: libPath,
                jr: {
                    licenseSN: licenseSN,
                    licenseKey: licenseKey
                }
            },
            renderTo: document.body,
            appearance: CustomAppearance,
            addons: libPath + '/uix-addons/allInOne.js'
        });
    </script>
    <style>
        input {
            width: 20em;
        }
        .fv__ui-dropdown {
            width: auto;
        }
    </style>

    {
        "iframeOptions": {
            "style": "height: 300px"
        }
    }