CefSharp 不编组 DOM 节点

当我在 CEFSharp 中使用 执行 js 时EvaluateScriptAsync(),我可以返回原始类型,如字符串或数组。例如,以下作品:


var result = await Browser.EvaluateScriptAsync("Array.from(document.getElementsByTagName('input')).map(element => element.value)");

if (result.Success && result.Result != null)

{

    dynamic values = result.Result;

    foreach (dynamic value in values)

    {

        MessageBox.Show($"Value is: {value}");

    }

}

但是一旦我尝试获取一个 DOM 元素,无论是一个元素还是列表元素,我都会得到null:


var result = await Browser.EvaluateScriptAsync("Array.from(document.getElementsByTagName('input'))");

// `result.Success` is `true`, `result.Result` is `null`

我认为 CEFSharp 只知道如何编组原始类型,但对象文字也可以工作:


var result = await Browser.EvaluateScriptAsync("({ a: 1, b: 'hello' })");

if (result.Success && result.Result != null)

{

    dynamic obj = result.Result;

    MessageBox.Show($"{{ a: {obj.a}, b: {obj.b} }}");

}

所以事实证明,CEFSharp 只是不知道如何编组 DOM 对象。


为什么?那里有解决方案或解决方法吗?


FFIVE
浏览 298回答 2
2回答

斯蒂芬大帝

首先,重要的是要了解它Javascript是在渲染过程中执行的。结果EvaluateScriptAsync实际上是一个DTO,我们创建一个对象来表示执行脚本的结果。目前无法返回 HTMLElement或任何具有循环引用的对象。如果我们将 `HTMLElement 作为一个特定的例子来看,它将有一个 parentElement/parentNode 并且父元素有包含节点本身的子元素。您最终也会遍历整个 DOM 树。CEF 对其CefV8Value类型的类型支持非常有限,因此很难做任何太花哨的事情。我们可能会添加一个扩展方法,将用户脚本包装在 中 IIFE并进行一些instanceof HTMLElement样式类型检查以返回 HTML 元素的精简表示。

慕哥6287543

作为使用 JavaScript 的替代方法,您现在可以使用CefSharp.Dom,这是一个用于访问 DOM 的异步库。它是免费提供的// Add using CefSharp.Dom to access CreateDevToolsContextAsync and related extension methods.await using var devToolsContext = await chromiumWebBrowser.CreateDevToolsContextAsync();// Get element by Id// https://developer.mozilla.org/en-US/docs/Web/API/Document/querySelectorvar element = await devToolsContext.QuerySelectorAsync<HtmlElement>("#myElementId");//Strongly typed element types (this is only a subset of the types mapped)var htmlDivElement = await devToolsContext.QuerySelectorAsync<HtmlDivElement>("#myDivElementId");var htmlSpanElement = await devToolsContext.QuerySelectorAsync<HtmlSpanElement>("#mySpanElementId");var htmlSelectElement = await devToolsContext.QuerySelectorAsync<HtmlSelectElement>("#mySelectElementId");var htmlInputElement = await devToolsContext.QuerySelectorAsync<HtmlInputElement>("#myInputElementId");var htmlFormElement = await devToolsContext.QuerySelectorAsync<HtmlFormElement>("#myFormElementId");var htmlAnchorElement = await devToolsContext.QuerySelectorAsync<HtmlAnchorElement>("#myAnchorElementId");var htmlImageElement = await devToolsContext.QuerySelectorAsync<HtmlImageElement>("#myImageElementId");var htmlTextAreaElement = await devToolsContext.QuerySelectorAsync<HtmlImageElement>("#myTextAreaElementId");var htmlButtonElement = await devToolsContext.QuerySelectorAsync<HtmlButtonElement>("#myButtonElementId");var htmlParagraphElement = await devToolsContext.QuerySelectorAsync<HtmlParagraphElement>("#myParagraphElementId");var htmlTableElement = await devToolsContext.QuerySelectorAsync<HtmlTableElement>("#myTableElementId");// Get a custom attribute valuevar customAttribute = await element.GetAttributeAsync<string>("data-customAttribute");//Set innerText property for the elementawait element.SetInnerTextAsync("Welcome!");//Get innerText property for the elementvar innerText = await element.GetInnerTextAsync();//Get all child elementsvar childElements = await element.QuerySelectorAllAsync("div");//Change CSS style background colourawait element.EvaluateFunctionAsync("e => e.style.backgroundColor = 'yellow'");//Type text in an input fieldawait element.TypeAsync("Welcome to my Website!");//Click The elementawait element.ClickAsync();// Simple way of chaining method calls together when you don't need a handle to the HtmlElementvar htmlButtonElementInnerText = await devToolsContext.QuerySelectorAsync<HtmlButtonElement>("#myButtonElementId")    .AndThen(x => x.GetInnerTextAsync());//Event Handler//Expose a function to javascript, functions persist across navigations//So only need to do this onceawait devToolsContext.ExposeFunctionAsync("jsAlertButtonClick", () =>{    _ = devToolsContext.EvaluateExpressionAsync("window.alert('Hello! You invoked window.alert()');");});var jsAlertButton = await devToolsContext.QuerySelectorAsync<HtmlButtonElement>("#jsAlertButton");//Write up the click event listner to call our exposed function_ = jsAlertButton.AddEventListenerAsync("click", "jsAlertButtonClick");//Get a collection of HtmlElementsvar divElements = await devToolsContext.QuerySelectorAllAsync<HtmlDivElement>("div");foreach (var div in divElements){    // Get a reference to the CSSStyleDeclaration    var style = await div.GetStyleAsync();    //Set the border to 1px solid red    await style.SetPropertyAsync("border", "1px solid red", important: true);    await div.SetAttributeAsync("data-customAttribute", "123");    await div.SetInnerTextAsync("Updated Div innerText");}//Using standard arrayvar tableRows = await htmlTableElement.GetRowsAsync().ToArrayAsync();foreach (var row in tableRows){    var cells = await row.GetCellsAsync().ToArrayAsync();    foreach (var cell in cells)    {        var newDiv = await devToolsContext.CreateHtmlElementAsync<HtmlDivElement>("div");        await newDiv.SetInnerTextAsync("New Div Added!");        await cell.AppendChildAsync(newDiv);    }}//Get a reference to the HtmlCollection and use async enumerable//Requires Net Core 3.1 or highervar tableRowsHtmlCollection = await htmlTableElement.GetRowsAsync();await foreach (var row in tableRowsHtmlCollection){    var cells = await row.GetCellsAsync();    await foreach (var cell in cells)    {        var newDiv = await devToolsContext.CreateHtmlElementAsync<HtmlDivElement>("div");        await newDiv.SetInnerTextAsync("New Div Added!");        await cell.AppendChildAsync(newDiv);    }}
打开App,查看更多内容
随时随地看视频慕课网APP

相关分类

JavaScript