如何让ActiveX COM接收DOM节点移除的通知(innerHTML/removeChild场景)
如何让ActiveX COM在DOM移除时收到通知
这个问题确实有点棘手,不过ActiveX虽然没有原生提供DOM移除的回调接口,但我们可以结合IE的DOM事件系统和控件的宿主交互逻辑来实现,FireBreath也是基于类似思路做到的,下面给你两个可行的方案:
方案1:监听宿主元素的DOM移除事件
在你的ActiveX控件初始化完成后,先拿到对应的HTML元素对象,然后给它绑定DOMNodeRemoved事件(IE原生支持这个事件),当元素被从DOM树里移除时,事件会触发,你就能在事件处理函数里通知控件执行清理逻辑了。
具体步骤和核心代码示例(C++):
- 在控件的
SetClientSite方法中,通过IOleClientSite接口拿到IWebBrowser2,进而获取当前的DOM文档和控件对应的元素。 - 给元素绑定
DOMNodeRemoved事件,在事件回调里判断是否是当前元素被移除,若是则执行控件的清理操作。
STDMETHODIMP CYourActiveX::SetClientSite(IOleClientSite* pClientSite) { HRESULT hr = IOleObjectImpl::SetClientSite(pClientSite); if (SUCCEEDED(hr) && pClientSite) { // 获取浏览器对象 IUnknown* pUnk; hr = pClientSite->GetContainer(&pUnk); if (SUCCEEDED(hr)) { IWebBrowser2* pBrowser; hr = pUnk->QueryInterface(IID_IWebBrowser2, (void**)&pBrowser); if (SUCCEEDED(hr)) { IDispatch* pDocDisp; hr = pBrowser->get_Document(&pDocDisp); if (SUCCEEDED(hr)) { IHTMLDocument2* pDoc; hr = pDocDisp->QueryInterface(IID_IHTMLDocument2, (void**)&pDoc); if (SUCCEEDED(hr)) { // 获取当前控件对应的DOM元素 IHTMLElement* pElem; hr = pDoc->get_activeElement(&pElem); if (SUCCEEDED(hr)) { // 绑定DOMNodeRemoved事件,当前控件需实现IDispatch来处理事件 IDispatch* pEventHandler = static_cast<IDispatch*>(this); pElem->attachEvent(_T("onDOMNodeRemoved"), pEventHandler); pElem->Release(); } pDoc->Release(); } pDocDisp->Release(); } pBrowser->Release(); } pUnk->Release(); } } return hr; } // 实现IDispatch的Invoke方法处理事件回调 STDMETHODIMP CYourActiveX::Invoke(DISPID dispidMember, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS* pDispParams, VARIANT* pVarResult, EXCEPINFO* pExcepInfo, UINT* puArgErr) { if (dispidMember == DISPID_ONDOMNODEREMOVED) { // 获取被移除的元素,判断是否是当前控件对应的元素 IHTMLElement* pRemovedElem = static_cast<IHTMLElement*>(pDispParams->rgvarg[0].pdispVal); // 这里可以添加元素对比逻辑,确认是当前控件的元素后执行清理 this->OnElementRemoved(); pRemovedElem->Release(); return S_OK; } return DISP_E_MEMBERNOTFOUND; }
方案2:利用控件的引用计数与销毁回调
当元素被从DOM移除后,IE不会立刻销毁控件,但当控件的引用计数降到0时,FinalRelease方法会被调用。不过这种方式的问题是,移除元素后控件可能还会在内存里待一段时间,直到所有引用都被释放——如果需要即时通知,方案1会更合适。
FireBreath的实现思路其实是给插件套了一层DOM包装器,监听这个包装器的DOMNodeRemoved事件,一旦触发就主动调用插件的销毁逻辑,同时严格管理引用计数,确保资源能及时被清理。
额外注意点
- 不同IE版本对DOM事件的支持有差异:
DOMNodeRemoved在IE9及以上表现稳定,IE8及以下可以考虑用onpropertychange事件,结合检查元素的parentNode是否为null来判断是否被移除。 - 绑定事件后一定要在控件销毁时解绑,避免内存泄漏。
内容的提问来源于stack exchange,提问作者Esse Swann
相关产品推荐
相关产品推荐

