Blazor University (40)JavaScript 互操作 —— 傳遞 HTML 元素引用

原文鏈接:https://blazor-university.com/javascript-interop/calling-javascript-from-dotnet/passing-html-element-references/

傳遞 HTML 元素引用

源代碼[1]

在編寫 Blazor 應用程序時,不鼓勵對文檔對象模型 (DOM) 進行操作,因為它可能會干擾其增量渲染樹[2],對 HTML 的任何更改都應在我們組件內的 .NET 代碼中進行管理。

有時我們可能希望繼續讓 JavaScript 與我們生成的 HTML 交互。實現這一點的標準 JavaScript 方法是給我們的 HTML 元素一個 id,并讓 JavaScript 使用 document.getElementById('someId') 來定位它。在靜態生成的 HTML 頁面中,這非常簡單,但是當通過組合許多組件的輸出來動態創建頁面時,很難確保 ID 在所有組件中都是唯一的。Blazor 使用 @ref 元素標記和 ElementReference 結構解決了這個問題。

@ref 和元素引用

當我們需要對 HTML 元素的引用時,我們應該使用 @ref 裝飾該元素(或 Blazor 組件)。我們通過創建一個類型為 ElementReference 的成員并使用 @ref 屬性在元素上識別它來識別我們組件中的哪個成員將持有對 HTML 元素的引用。

@page?"/"<h1?@ref=MyElementReference>Hello,?world!</h1>
Welcome?to?your?new?app.@code?{ElementReference?MyElementReference;
}
  • 第 3 行

    定義一個 HTML 元素并使用 @ref 指定在引用該元素時我們將使用組件中的哪個成員 (MyElementReference)。

  • 第 7 行

    引用用 @ref 裝飾的元素時將使用的成員。

如果我們更改新 Blazor 應用程序的 Index.razor 文件以添加對 h1 元素的元素引用并運行應用程序,我們將看到類似于以下生成的 HTML 的內容。

<h1?_bl_bc0f34fa-16bd-4687-a8eb-9e3838b5170d="">Hello,?world!</h1>

添加此特殊格式的屬性是 Blazor 如何唯一標識元素而無需劫持元素的 id 參數。我們現在將使用 @ref``、ElementReference 和 JavaScript 互操作來解決一個常見問題。

案例:元素自動聚焦

HTML 規范有一個 autofocus 屬性,可以應用于任何可聚焦的元素;當一個頁面被加載時,瀏覽器會找到第一個用 autofocus 裝飾的元素并給它焦點。由于 Blazor 應用程序不會真正導航(HTML 被簡單地重寫并且瀏覽器 URL 更改),當我們導航到新 URL 并向用戶呈現新內容時,瀏覽器不會掃描 autofocus 屬性。這意味著將 autofocus 屬性放在輸入上不起作用。這是我們將使用 JavaScript Interop、@refElementReference 解決的問題。

觀察自動聚焦問題

  • 首先創建一個新的 Blazor 應用程序。

  • 在每個頁面中,用每個 @page 指令下方的相同標記替換內容。

Enter?your?name:?<input?autofocus?/>

運行應用程序并觀察 <input> 元素如何不會自動獲得焦點,甚至在第一頁加載時也不會。

解決自動聚焦問題

  • wwwroot 文件夾中創建一個腳本文件夾。

  • 在該文件夾中創建一個名為 AutoFocus.js 的新文件并輸入以下腳本。

var?BlazorUniversity?=?BlazorUniversity?||?{};
BlazorUniversity.setFocus?=?function?(element)?{element.focus();
};

確保在 /Pages/_Host.cshtml(服務器端 Blazor 應用程序)或 /wwwroot/index.html(WebAssembly Blazor 應用程序)中添加對此腳本的引用。

Index.razor 頁面中更改標記如下:

@page?"/"
@inject?IJSRuntime?JSRuntime
Enter?your?name
<input?@ref=ReferenceToInputControl?/>@code
{ElementReference?ReferenceToInputControl;protected?override?async?Task?OnAfterRenderAsync(bool?firstRender){if?(firstRender)await?JSRuntime.InvokeVoidAsync("BlazorUniversity.setFocus",?ReferenceToInputControl);}
}
  • 第 4 行

    使用 @ref 裝飾器為輸入提供一個在組件內唯一的標識。

  • 第 8 行

    這是將持有元素標識的成員,該成員必須是 ElementReference 類型。

  • 第 12 行

    如果這是該組件第一次渲染,則元素引用將傳遞給我們的 JavaScript,它為元素提供焦點。

現在,在頁面之間切換應該會導致第一頁上的輸入在呈現特定頁面時獲得焦點。

組件化我們的自動聚焦解決方案

添加 JavaScript 以在每個頁面上設置焦點并不需要太多工作,但它是重復的。此外,根據顯示的選項卡將自動對焦設置為選項卡控件中的第一個控件將需要更多工作。這是我們應該以可重用的形式編寫的那種東西。

首先,更改我們其中一個頁面的標記,使其使用新的 AutoFocus 控件。

@page?"/"
Enter?your?name
<input?@ref=ReferenceToInputControl?/>
<AutoFocus?Control=ReferenceToInputControl/>@code?{ElementReference?ReferenceToInputControl;
}

/Shared 文件夾中創建一個名為 Autofocus.razor 的新組件并輸入以下標記。

@inject?IJSRuntime?JSRuntime
@code?{[Parameter]public?ElementReference?Control?{?get;?set;?}protected?override?async?Task?OnAfterRenderAsync(bool?firstRender){if?(firstRender)await?JSRuntime.InvokeVoidAsync("BlazorUniversity.setFocus",?Control);}
}
  • 第 4 行

    為組件定義一個參數 Control,該參數接受一個 ElementReference 來標識哪個控件應該獲得焦點。

  • 第 9 行

    執行我們的 JavaScript 以將焦點設置到指定的控件。

這個解決方案的問題在于,組件參數的值是在渲染樹構建過程中傳遞的,而元素引用在構建渲染樹并且結果已經在瀏覽器中渲染為 HTML 之后才有效。此解決方案導致錯誤 element.focus is not a function,因為 ElementReference 在其值被傳遞給我們的 AutoFocus 組件時無效。

注意:不要過早使用元素引用!

正如我們在渲染樹[3]部分中看到的,在其渲染階段,Blazor 根本不會更新瀏覽器 DOM。只有在所有組件的渲染完成后,Blazor 才會比較新的和以前的渲染樹,然后用盡可能少的更改更新 DOM。

這意味著在構建渲染樹時,使用 @ref 引用的元素可能還不存在于瀏覽器 DOM 中——因此任何通過 JavaScript 與它們交互的嘗試都將失敗。因此,我們不應該嘗試在除 OnAfterRenderOnAfterRenderAsync 之外的任何組件生命周期方法中使用 ElementReference 的實例,并且由于組件的參數是在構建渲染樹期間設置的,我們不能將 ElementReference 作為參數傳遞,因為它是在組件的生命周期中為時過早。當然,從用戶事件(例如按鈕單擊)訪問引用是可以接受的,因為該頁面已經生成為 HTML。

事實上,直到調用 OnAfterRender* 方法之前,甚至不會設置 ElementReference 的實例。Blazor 流程如下:

  • 為頁面生成虛擬渲染樹。

  • 將更改應用到瀏覽器的 HTML DOM。

  • 對于每個 @ref 修飾元素,更新 Blazor 組件中的 ElementReference 成員。

  • 執行 OnAfterRender* 生命周期方法。

我們可以通過更改標準 Blazor 應用程序的 Index.razor 組件來證明這個過程,在組件生命周期的各個點將 ElementReference 序列化為字符串,并將序列化的文本呈現到屏幕上。將新項目中的 Index.razor 更改為以下標記并運行應用程序。

@page?"/"<h1?@ref=MyElementReference>Hello,?world!</h1>
<button?@onclick=ButtonClicked>Show?serialized?reference</button><code><pre>@Log</pre></code>Welcome?to?your?new?app.@code?{string?Log;ElementReference?MyElementReference;protected?override?void?OnInitialized(){Log?+=?"OnInitialized:?";ShowSerializedReference();}protected?override?void?OnAfterRender(bool?firstRender){Log?+=?"OnAfterRender:?";ShowSerializedReference();}private?void?ButtonClicked(){Log?+=?"Button?clicked:?";ShowSerializedReference();}private?void?ShowSerializedReference(){Log?+=?System.Text.Json.JsonSerializer.Serialize(MyElementReference)?+?"\r\n";}
}
  1. 我們的組件實例已創建。執行 OnInitialized(第 15 行)。

  2. MyElementReference 的值被序列化為我們的 Log 字符串(第 33 行)。

  3. 生成渲染樹。

  4. 瀏覽器的 DOM 已更新

  5. Blazor 檢查使用 @ref 修飾的元素并更新它們標識的 ElementReference

  6. OnAfterRender 在我們的組件上執行(第 21 行)。

  7. MyElementReference 的值被序列化為我們的 Log 字符串,但不顯示 - 我們必須調用 StateHasChanged 才能看到它,但 Log 的值已經更新。

  8. 用戶單擊按鈕。

  9. MyElementReference 的值被序列化為我們的 Log 字符串。

  10. Blazor 執行 StateHasChanged 以響應按鈕單擊。

  11. 我們在屏幕上看到更新的 Log 以顯示從第 7 步和第 9 步添加的值——這兩個都顯示了一個非空標識符。

edcda5b98bbd90176f21dc084601626f.gif

完成 AutoFocus ?組件

我們可以傳入一個 Func<ElementReference>,而不是傳入 ElementReference 本身,我們的 AutoFocus 組件然后可以在其 OnAfterRender* 生命周期方法中執行此 Func——此時返回的值將是有效的。

AutoFocus 控件更改為接受 Func,并確保設置的值不為空。

@inject?IJSRuntime?JSRuntime
@code?{[Parameter]public?Func<ElementReference>?GetControl?{?get;?set;?}protected?override?async?Task?OnAfterRenderAsync(bool?firstRender){if?(GetControl?is?null)throw?new?ArgumentNullException(nameof(GetControl));if?(firstRender)await?JSRuntime.InvokeVoidAsync("BlazorUniversity.setFocus",?GetControl());}
}

該組件現在可以按如下方式使用:

@page?"/"
Enter?your?name
<input?@ref=ReferenceToInputControl?/>
<AutoFocus?GetControl=@(?()?=>?ReferenceToInputControl)/>@code?{ElementReference?ReferenceToInputControl;
}

注意:未來的 Blazor 計劃自動創建 ElementReference 成員。

參考資料

[1]

源代碼: https://github.com/mrpmorris/blazor-university/tree/master/src/JavaScriptInterop/HtmlElementReferences

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/284010.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/284010.shtml
英文地址,請注明出處:http://en.pswp.cn/news/284010.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

RabbitMQ+PHP 教程六(RPC)

(using php-amqplib) 前提必讀 本教程假設RabbitMQ是安裝在標準端口上運行&#xff08;5672&#xff09;。如果您使用不同的主機、端口或憑據&#xff0c;則連接設置需要調整。 如果您在本教程中遇到困難&#xff0c;可以通過郵件列表與我們聯系。 開始 在第二個教程中&#xf…

TKMybatis 介紹和使用

目錄 一、什么是 TKMybatis 二、TKMybatis 使用 2.1 Springboot 項目中加入依賴 2.2 使用講解 2.2.1 實體類中使用 2.2.2 dao中使用 2.2.3 Service 層中使用 2.3 實際案例 2.3.1 dao 層使用 2.3.2 service 層使用 一、什么是 TKMybatis TKMybatis 是基于 Mybatis 框…

angularjs的ng-repeat回調

首先html代碼是這樣的&#xff1a; <label>Name des Leiters:</label><select name"leaderID" id"selectLeaderID"><option ng-repeat"manager in managers" value"leader_id{{manager.id}}&leader_name{{manager…

sed和vim練習

1、刪除/etc/grub2.conf文件中所有以空白開頭的行行首的空白字符sed s^[[:space:]]\ /etc/grub2.conf2、刪除/etc/fstab文件中所有以#開頭&#xff0c;后面至少跟一個空白字符的行的行首的#和空白字符sed -n s^#[[:space:]]\p /etc/fstab3、在/root/install.log每一行行首增加#…

WinForm(三)揭開可視化控件的面紗

WinForm所見即所得的UI設計框架&#xff0c;開發效率確實有所提升&#xff0c;同時降低了編程門檻&#xff0c;讓WinForm更普及。拖拖拽拽就能設計出一個界面&#xff0c;那么我們拖拽的這些東西是什么&#xff1f;它們是什么原理&#xff1f;。WinForm我覺得很好的一點是&…

淺談 maxMemory , totalMemory , freeMemory 和 OOM 與 native Heap

作者&#xff1a;林冠宏 / 指尖下的幽靈 掘金&#xff1a;https://juejin.im/user/587f0dfe128fe100570ce2d8 博客&#xff1a;http://www.cnblogs.com/linguanh/ GitHub &#xff1a; https://github.com/af913337456/ 騰訊云專欄&#xff1a; https://cloud.tencent.com/deve…

RestTemplate 詳解

在項目中&#xff0c;當我們需要遠程調用一個 HTTP 接口時&#xff0c;我們經常會用到 RestTemplate 這個類。這個類是 Spring 框架提供的一個工具類。Spring 官網對它的介紹如下&#xff1a; RestTemplate: The original Spring REST client with a synchronous, template met…

初識Spark2.0之Spark SQL

內存計算平臺Spark在今年6月份的時候正式發布了spark2.0&#xff0c;相比上一版本的spark1.6版本&#xff0c;在內存優化&#xff0c;數據組織&#xff0c;流計算等方面都做出了較大的改變&#xff0c;同時更加注重基于DataFrame數據組織的MLlib&#xff0c;更加注重機器學習整…

webpack開發Vue配置

一直以來使用webpack都是用的別人的配置&#xff0c;這幾天自己學習了一下。 項目地址&#xff1a;https://github.com/donghaohao... 新建整個工程 npm init安裝依賴&#xff0c;這里我們開發vue項目&#xff0c;npm install vue --save&#xff0c;然后是開發時的依賴npm ins…

ABP詳細教程——模塊類

概述模塊化是ABP vNext的最大亮點&#xff0c;也是ABP vNext框架的核心&#xff0c;而模塊類是ABP vNext框架模塊化的核心要素。這一章節&#xff0c;我就從模塊類的用法、運行機制、源代碼等層面&#xff0c;帶大家詳細了解ABP vNext的模塊類。用法在ABP的約定中&#xff0c;每…

[轉]Eureka工作原理

目錄 Eureka 工作原理 Eureka 核心概念 自我保護機制 Eureka 集群原理 Eurka 工作流程 總結 Eureka 工作原理 上節內容為大家介紹了&#xff0c;注冊中心 Eureka 產品的使用&#xff0c;以及如何利用 Eureka 搭建單臺和集群的注冊中心。這節課我們來繼續學習 Eureka&…

centos7下別名(alias)的特殊用法

版權聲明&#xff1a;轉載請注明出處:http://blog.csdn.net/dajitui2024 https://blog.csdn.net/dajitui2024/article/details/79438200 參考&#xff1a;https://www.cyberciti.biz/faq/bash-bypass-alias-command-on-linux-macos-unix/ 正常情況下&#xff0c;定義過的別名&a…

解決WDCP3環境gbk網站編碼程序亂碼問題

因為默認WDCP V3版本環境編碼格式是UTF-8版本&#xff0c;如果我們程序采用的是GBK編碼肯定都會有亂碼問題。 我們到WDCP后臺&#xff0c;"網站管理"-"PHP設置"&#xff0c;看到上圖所示&#xff0c;準備直接在線編輯PHP.INI文件。 這里我們找到"defa…

重談聯想5G編碼投票事件

此前&#xff0c;司馬南談了聯想好幾個問題&#xff0c;其中最尖銳的要屬國有資產流失&#xff0c;這是聯想管理層無法回避的死穴。不過&#xff0c;司馬南批判聯想5G投票背刺H公司&#xff0c;這基本就是造謠了。當年&#xff0c;媒體把編碼投票炒作的很厲害&#xff0c;抨擊聯…

JStorm2.1.1集群的安裝和使用

為什么80%的碼農都做不了架構師&#xff1f;>>> JStorm2.1.1集群的安裝和使用 Storm是一個免費開源、分布式、高容錯的實時計算系統&#xff0c;而JStorm是阿里巴巴開源的基于Storm采用Java重寫的一套分布式實時流計算框架&#xff0c;在性能和支持的集群規模上做了…

Hystrix 原理

Hystrix是什么&#xff1f; Hystrix是Netflix開源庫&#xff0c;這是一個針對分布式系統的延遲和容錯庫。 Hystrix 供分布式系統使用&#xff0c;提供延遲和容錯功能&#xff0c;隔離遠程系統、訪問和第三方程序庫的訪問點&#xff0c;防止級聯失敗&#xff0c;保證復雜的分布…

「深度」無人機實名制政策特稿|市場看好、資本關注,“反黑飛”正在崛起

從政策和需求來看&#xff0c;“反黑飛”越來越重要&#xff0c;市場也正在不斷崛起。 對于大多數人來說&#xff0c;今天是最適合明目張膽“裝嫩”的六一兒童節。不過&#xff0c;在無人機廠商和無人機玩家的眼里&#xff0c;今天是無人機實名制政策正式實施的日子。 近年來&…

在navicat中新建數據庫

前言&#xff1a; 在本地新建一個名為editor的數據庫&#xff1b; 過程&#xff1a; 1.&#xff1b; 2.選擇&#xff1a;utf8mb4 -- UTF-8 Unicode字符集&#xff0c;原因在于&#xff1a;utf8mb4兼容utf8&#xff0c;且比utf8能表示更多的字符。&#xff0c;而且它支持表情符號…

MASA Stack 第三期社區例會

MASA Blazor 0.5.0發版內容功能Autocomplete&#xff1a;支持通過設置AutoSelectFirst參數開啟自動選擇第一項的功能&#xff0c;支持CacheItems參數&#xff0c;增強使用上下鍵的用戶體驗。BottomNavigation&#xff1a;&#xff1a;一個替代側邊欄的新組件。它主要用于移動應…

MySQL添加用戶、刪除用戶與授權

MySql中添加用戶,新建數據庫,用戶授權,刪除用戶,修改密碼(注意每行后邊都跟個;表示一個命令語句結束): 1.新建用戶 1.1 登錄MYSQL&#xff1a; >mysql -u root -p >密碼 1.2 創建用戶&#xff1a; mysql> insert into mysql.user(Host,User,Password) values("lo…