'WebView2 JS injection returns empty json string

With WebView2 for targeting Windows, I am trying to setup my own context menu. Either by selected text or by underlying element pointed by mouse click.

However, I can't get DOM element by mouse operation or even by byId. I think my JavaScript injection or WebView property setting is something wrong, but not too sure. Can anyone suggest me the resolution?

The version info.

  • OS : Windows 10 Pro 21H2 19044.1682
  • Visual Studio : Community 2022 17.1.6
  • WebView2 : 1.0.1185.39
  • Project Property: Target framework=.NET 6.0; Target OS version=10.0.19041.0

Here's the testing code

using Microsoft.Web.WebView2.Core;
namespace WinFormsApp1
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
            HTML(@"C:\temp\test.html");
        }
        public void HTML(string url)
        {
            webView21.CoreWebView2InitializationCompleted += WebView2Control_CoreWebView2InitializationCompleted;
            webView21.Source = new Uri(url);
        }
        private void WebView2Control_CoreWebView2InitializationCompleted(object? sender, CoreWebView2InitializationCompletedEventArgs e)
        {
            if (!e.IsSuccess)
            {
                MessageBox.Show($"WebView2 creation failed, with exception : {e.InitializationException}");
                return;
            }
            // subscribe to events we are interested in
            webView21.CoreWebView2.ContextMenuRequested += CoreWebView2_ContextMenuRequested;       // user clicked right mouse to show context menu
        }
        private async void CoreWebView2_ContextMenuRequested(object? sender, Microsoft.Web.WebView2.Core.CoreWebView2ContextMenuRequestedEventArgs e)
        {
            IList<CoreWebView2ContextMenuItem> menuItemList = e.MenuItems;
            menuItemList.Clear();  // clear default menu items, like prev, next, property

            //GETTING SELECTED TEXT
            string text = e.ContextMenuTarget.HasSelection ? e.ContextMenuTarget.SelectionText : ""; // it works

            if (string.IsNullOrEmpty(text))  // no text selection, then examine DOM
            {
            //GET AN UNDERLYING ELEMENT FROM MOUSE POINT
                var result = await webView21.CoreWebView2.ExecuteScriptAsync($"document.elementFromPoint({e.Location.X},{e.Location.Y})");   //it doesn't work, just returns an empty JSON text (not null)
                //var result = await webView21.CoreWebView2.ExecuteScriptAsync("function foo(){return 'foo() gets called';}; foo();");  //for testing purpose, it works
                //var result = await webView21.CoreWebView2.ExecuteScriptAsync("function foo(){return document.getElementById('table-content'};foo();)");   //it returns an empty result
            }
            // TO DO
            // setup menuItem tree based on the result we got
            //......
            //......
            e.Handled = true;
        }
    }
}


Solution 1:[1]

I think i managed it, thanks for insights given. My original intent was to capture innerText and href and to feed them to C# code(host). one note. getter/setter has to be ready. Here's complete test code.

using Microsoft.Web.WebView2.Core;
using System.Text.Json;

namespace WinFormsApp1
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
            HTML(@"C:\temp\test.html");
        }
        public void HTML(string url)
        {
            webView21.CoreWebView2InitializationCompleted += WebView2Control_CoreWebView2InitializationCompleted;
            this.webView21.Source = new Uri(url);
        }
        private void WebView2Control_CoreWebView2InitializationCompleted(object? sender, CoreWebView2InitializationCompletedEventArgs e)
        {
            if (!e.IsSuccess)
            {
                MessageBox.Show($"WebView2 creation failed, with exception : {e.InitializationException}");
                return;
            }
            // subscribe to events we are interested in
            webView21.CoreWebView2.ContextMenuRequested += CoreWebView2_ContextMenuRequested;       // user clicked right mouse to show context menu
        }
        public class HTMLelements
        {
            public string href { get; set; } = "";
            public string innerText { get; set; } = "";
        }
        private async void CoreWebView2_ContextMenuRequested(object? sender, Microsoft.Web.WebView2.Core.CoreWebView2ContextMenuRequestedEventArgs e)
        {
            IList<CoreWebView2ContextMenuItem> menuItemList = e.MenuItems;
            menuItemList.Clear();  // clear default menu items, like prev, next, property

            HTMLelements htmlElements = new(); //Didn't know this new syntax

            //GETTING SELECTED TEXT
            htmlElements.innerText = e.ContextMenuTarget.HasSelection ? e.ContextMenuTarget.SelectionText : "";
            if (string.IsNullOrEmpty(htmlElements.innerText))  // no text selection, then examine DOM
            {
                //GETTING AN UNDERLYING ELEMENT FROM MOUSE POINT
                //it should return JSON string { "href" : "something or empty", "innerText: "something" }
                string jScript = $@"
                    function GetElement() {{
                        var elem = document.elementFromPoint({ e.Location.X},{ e.Location.Y});
                        var obj = new Object();
                        obj.href = elem.href === undefined? '' : elem.href;
                        obj.innerText = elem.innerText;
                        return obj;
                    }};
                    GetElement();";

                var result = await webView21.CoreWebView2.ExecuteScriptAsync(jScript);
                htmlElements = JsonSerializer.Deserialize<HTMLelements>(result); // feed them to our class object
            }
            // TO DO
            //setup menuItem tree based on the result we got
            //setting up CoreWebView2ContextMenuItem is new to me, hence I use C# standard toolStripMenuItem instead.
            //sometime later, I will amend as such
            toolStripMenuItemPlaceHolder.Text = htmlElements.innerText;
            toolStripMenuItemPlaceHolder.Enabled = true;
            if (!string.IsNullOrEmpty(htmlElements.href))
            {
                toolStripMenuItemPlaceHolder.Tag = htmlElements.href;
                toolStripMenuItemShowInBrowser.Enabled = true;
            }
            else
            {
                toolStripMenuItemPlaceHolder.Tag = null;
                toolStripMenuItemShowInBrowser.Enabled = false;
            }
            contextMenuStripInBrowser.Show(webView21, e.Location);

            e.Handled = false;
        }
    }
}

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1