You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何为IWebElement添加事件处理程序或监听器以替代Timer监听数值变化

实现方案

Selenium本身没有为IWebElement提供原生的文本变化监听事件,但可以通过注入浏览器端的MutationObserver实现变更主动通知,完全替代Timer轮询,运行效率更高。

核心思路

利用JavaScript原生的MutationObserver接口监听对应span元素的文本内容变化,变更发生时主动记录变更信息,C#端只需读取变更记录处理即可,无需高频轮询全量元素。优先针对你最关注的价格字段实现,其余字段可按相同逻辑扩展。

具体实现代码

1. 为目标元素注入监听逻辑

遍历表格行时,为需要监听的元素绑定MutationObserver:

for (int i = 0; i < stockWatchListTableRows.Count; i++)
{
    if (i != 0 && i != 1 && i != stockWatchListTableRows.Count - 1)
    {
        var row = stockWatchListTableRows[i];
        var tds = row.FindElements(By.TagName("span"));
        var priceElement = tds[2]; // 优先监听价格元素
        var symbol = tds[0].GetAttribute("textContent");

        // 执行JS注入监听
        ((IJavaScriptExecutor)driver).ExecuteScript(@"
            const targetEl = arguments[0];
            const symbol = arguments[1];
            // 清理之前的重复监听
            if (targetEl._priceObserver) targetEl._priceObserver.disconnect();

            const observer = new MutationObserver((mutations) => {
                mutations.forEach(mutation => {
                    if (mutation.type === 'childList' || mutation.type === 'characterData') {
                        const newPrice = targetEl.textContent.trim();
                        // 全局存储变更记录,供C#端读取
                        if (!window.__stockPriceChanges) window.__stockPriceChanges = [];
                        window.__stockPriceChanges.push({
                            symbol: symbol,
                            newPrice: newPrice
                        });
                    }
                });
            });

            // 配置监听范围,覆盖文本变化的所有场景
            observer.observe(targetEl, {
                childList: true,
                characterData: true,
                subtree: true
            });
            targetEl._priceObserver = observer;
        ", priceElement, symbol);
    }
}

2. 处理变更事件

无需高频轮询全量列表,只需读取变更记录处理即可,以下是无阻塞的异步处理实现:

// 持续监听价格变更,不会阻塞主线程
while (appRunning) // 替换为你自己的程序运行状态判断变量
{
    var change = (Dictionary<string, object>)((IJavaScriptExecutor)driver).ExecuteAsyncScript(@"
        const callback = arguments[0];
        // 有未处理的变更直接返回
        if (window.__stockPriceChanges?.length > 0) {
            callback(window.__stockPriceChanges.shift());
            return;
        }
        // 无变更则等待新变更触发后返回
        const checkTimer = setInterval(() => {
            if (window.__stockPriceChanges?.length > 0) {
                clearInterval(checkTimer);
                callback(window.__stockPriceChanges.shift());
            }
        }, 100);
    ");

    // 执行业务逻辑
    string symbol = change["symbol"].ToString();
    string newPrice = change["newPrice"].ToString();
    channelingStocksData csdata = stockDictionary[symbol];
    var tblist = csdata.tbl;
    TextBox currentPriceTB = tblist[(int)TLPColumnNames.Current_Price];
    string existingPrice = currentPriceTB.Text;
    bool bPriceIncreasing = Convert.ToDouble(newPrice) > Convert.ToDouble(existingPrice);
    ScreenStockData(symbol, newPrice, bPriceIncreasing);
}

注意事项

  • 若页面刷新或者目标元素被销毁重建,需要重新执行绑定监听的逻辑,可在页面加载完成的回调中触发重绑定
  • 其余三个字段的监听可完全复用上述逻辑,只需替换监听的元素和全局存储的字段名即可
  • 该方案仅在元素内容真实变化时才会触发处理逻辑,无无用的重复读取操作,性能远高于Timer轮询方案

内容的提问来源于stack exchange,提问作者GRF

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.28 12:06:04