Handling HTML Element Events

http://msdn.microsoft.com/en-us/library/bb508508(v=vs.85).aspx

Handling HTML Element Events

9 out of 10 rated this helpful – Rate this topic

The HTMLElementEvents2 interface is an event sink interface that enables an application to receive events for HTML elements. Your custom application can receive these events, which are fired in response to user actions on HTML elements, when hosting the WebBrowser Control or automating Windows Internet Explorer.

The HTMLElementEvents2 interface is introduced and the steps required to receive notification of HTML element events from the browser are described, using Microsoft Visual Studio 5 or later.

It is assumed that a Microsoft Foundation Classes (MFC) application is hosting the WebBrowser Control, or launching Internet Explorer.

This article is divided into the following sections.

Accessing the DHTML Object Model

The IWebBrowser2::Navigate2 method of the IWebBrowser2 interface allows you to navigate the browser to a URL. Once an HTML page is loaded, you can access the HTML elements through the Dynamic HTML (DHTML) Object Model.

The DHTML Object Model is used to access and manipulate the contents of an HTML page and is not available until the page is loaded. Your application determines that the page is loaded by handling the DWebBrowserEvents2::DocumentComplete event of theWebBrowser Control. This event may be fired once for each frame in the page, and once when the top frame of the document is loaded. You can determine if the DWebBrowserEvents2::DocumentComplete event is for the top frame by comparing the IDispatchinterface pointer passed by this event with that of the WebBrowser Control.

This sample handler code for the DWebBrowserEvents2::DocumentComplete event demonstrates how to determine if this event is for the top frame, which indicates that the HTML page has loaded. This sample also demonstrates how to create a stream from a block of memory—in this case a string that contains the HTML content to be displayed. The variable m_pBrowser contains the IWebBrowser2 interface pointer obtained from the WebBrowser Control.

void CMyClass::DocumentComplete(LPDISPATCH pDisp, VARIANT* URL)
{
    HRESULT hr;
    IUnknown* pUnkBrowser = NULL;
    IUnknown* pUnkDisp = NULL;
    IDispatch* pDocDisp = NULL;
    IHTMLDocument2* pDoc = NULL;

    // Is this the DocumentComplete event for the top frame window?
    // Check COM identity: compare IUnknown interface pointers.
    hr = m_pBrowser->QueryInterface(IID_IUnknown, (void**)&pUnkBrowser);

    if (SUCCEEDED(hr))
    {
        hr = pDisp->QueryInterface(IID_IUnknown, (void**)&pUnkDisp);

        if (SUCCEEDED(hr))
        {
            if (pUnkBrowser == pUnkDisp)
            {
                // This is the DocumentComplete event for the top frame.
                // This page is loaded, so we can access the DHTML Object Model.
                hr = m_pBrowser->get_Document(&pDocDisp);

                if (SUCCEEDED(hr))
                {
                    // Obtained the document object.
                    pDocDisp->QueryInterface(IID_IHTMLDocument2, (void**)&pDoc);
                    if (SUCCEEDED(hr))
                    {
                        // Obtained the IHTMLDocument2 interface for the document object
                        ProcessDocument(pDoc);
                    }

                    pDocDisp->Release();
                }
            }

            pUnkDisp->Release();
        }

        pUnkBrowser->Release();
    }
}

The IWebBrowser2::get_Document property on the WebBrowser Control retrieves the document object that represents the DHTML Object Model for the top frame.

Accessing an Element on the Page

Using the IHTMLDocument2 interface pointer, you can request a collection of all elements in the HTML document through the all property.

void CMyClass::ProcessDocument(IHTMLDocument2* pDoc)
{
    IHTMLElementCollection* pElemColl = NULL;

    hr = pDoc->get_all(&pElemColl);
    if (SUCCEEDED(hr))
    {
        // Obtained element collection.
        ProcessElementCollection(pElemColl);
        pElemColl->Release();
    }
}

The all property returns a collection of all the HTML elements on the page through an IHTMLElementCollection interface pointer. You can use the IHTMLElementCollection interface to call the item method and pass the name or id of an element as a parameter, as shown in the following code.

Note  The item method will return a collection if there is more than one element with the specified name or id. To prevent a collection from being returned, provide an index as the second parameter of item to specify which element should be returned.
void CMyClass::ProcessElementCollection(IHTMLElementCollection* pElemColl)
{
    IDispatch* pElemDisp = NULL;
    IHTMLElement* pElem = NULL;
    _variant_t varID("myID", VT_BSTR);
    _variant_t varIdx(0, VT_I4);

    hr = pElemColl->item(varID, varIdx, &pElemDisp);

    if (SUCCEEDED(hr))
    {
        hr = pElemDisp->QueryInterface(IID_IHTMLElement, (void**)&pElem);

        if (SUCCEEDED(hr))
        {
            // Obtained element with ID of "myID".
            ConnectEvents(pElem);
            pElem->Release();
        }

        pElemDisp->Release();
    }
}

If you are working with HTML tags of a specific type, such as a tags, the IHTMLElementCollection::tags method returns a collection of all the elements that have the requested HTML tag name, also through an IHTMLElementCollection interface pointer.

Receiving Element Events

Each element in the DHTML Object Model supports an outgoing HTMLElementEvents2 interface. This interface defines the events that an HTML element can fire. You implement this interface to provide an event sink, which is a Component Object Model (COM) object that implements an outgoing interface and is used as the mechanism for firing events.

Note  Interfaces implemented by a server usually have their methods called by the client, but to fire an event, the server calls the respective method on the client event sink. These interface are called outgoing interfaces. A COM object that implements an outgoing interface is also known as a connectable object.

The following steps are required to receive events from an outgoing interface:

  1. Implement the event sink.

    The event sink implements the appropriate outgoing interface and methods. Internet Explorer event interfaces are dispinterfaces, so calls to event methods are made through IDispatch::Invoke. This means that you only need to implement the IDispatch interface to handle events.

  2. Determine if the server is a connectable object.

    Call QueryInterface to retrieve a pointer to the IConnectionPointContainer interface.

  3. Find the appropriate connection point.

    Call the IConnectionPointContainer::FindConnectionPoint method to find the connection point you need. For Internet Explorer WebBrowser Control events, such as DWebBrowserEvents2::DocumentComplete, this is DWebBrowserEvents2. For element events, this is HTMLElementEvents2. You can also call the IConnectionPointContainer::EnumConnectionPoints to enumerate through all the connection points a server supports.

  4. Advise the connection point that you want to receive events.

    Using the IConnectionPoint interface pointer returned in the previous step, call IConnectionPoint::Advise, passing the IUnknown interface pointer of your event sink.

    Note  The connectable object will use the IUnknown interface pointer to query the client for the event sink interface. If the event sink does not support the outgoing interface, Internet Explorer will query the client for the IDispatch interface.
  5. When you no longer want to receive events, you can call the IConnectionPoint::Unadvise method, passing the cookie you received from the call to IConnectionPoint::Advise.

The following sample code demonstrates how to begin receiving HTML element events for an element on an HTML page.

void CMyClass::ConnectEvents(IHTMLElement* pElem)
{
    HRESULT hr;
    IConnectionPointContainer* pCPC = NULL;
    IConnectionPoint* pCP = NULL;
    DWORD dwCookie;

    // Check that this is a connectable object.
    hr = pElem->QueryInterface(IID_IConnectionPointContainer, (void**)&pCPC);

    if (SUCCEEDED(hr))
    {
        // Find the connection point.
        hr = pCPC->FindConnectionPoint(DIID_HTMLElementEvents2, &pCP);

        if (SUCCEEDED(hr))
        {
            // Advise the connection point.
            // pUnk is the IUnknown interface pointer for your event sink
            hr = pCP->Advise(pUnk, &dwCookie);

            if (SUCCEEDED(hr))
            {
                // Successfully advised
            }

            pCP->Release();
        }

        pCPC->Release();
    }
}

The following sample code demonstrates how you would detect the firing of an HTMLElementEvents2::onclick event in your implementation of IDispatch::Invoke.

STDMETHODIMP CEventSink::Invoke(DISPID dispidMember,
                                REFIID riid,
                                LCID lcid,
                                WORD wFlags,
                                DISPPARAMS* pdispparams,
                                VARIANT* pvarResult,
                                EXCEPINFO* pexcepinfo,
                                UINT* puArgErr)
{
    switch (dispidMember)
    {
        case DISPID_HTMLELEMENTEVENTS2_ONCLICK:
        OnClick();
        break;

        default:
        break;
    }

    return S_OK;
}

Handling Events using MFC

  • The MFC CCmdTarget class implements the IDispatch interface, which means that any class derived from this class can implement an event sink. The IDispatch feature requires a call to the CCmdTarget::EnableAutomation method.
  • The MFC AfxConnectionAdvise and AfxConnectionUnadvise functions find a specified connection point and then advise or unadvise appropriately.
  • The DECLARE_DISPATCH_MAP, BEGIN_DISPATCH_MAP, DISP_FUNCTION, DISP_FUNCTION_ID and END_DISPATCH_MAP macros are used to map each event method to your event handler function.
  • When handling events from a Microsoft ActiveX control you are hosting in your MFC applications, use the DECLARE_EVENTSINK_MAP, BEGIN_EVENTSINK_MAP, ON_EVENT and END_EVENTSINK_MAP macros.
  • If you are hosting the WebBrowser Control on a dialog box, you can use the Microsoft Visual C++ ClassWizard to map events to event handlers.
  • The MFC CHtmlView class hosts the WebBrowser Control and provides overridable methods for the WebBrowser Control events.

Handling Events using ATL

  • When implementing an event sink using the Active Template Library (ATL), your object class will usually derive from the IDispatchImpl class, which provides a default implementation of the IDispatch interface. You can the override the ATL implementation of theIDispatch::Invoke method to handle events.
  • ATL provides two helper functions, AtlAdvise and AtlUnadvise, for advising and unadvising a connection point. These find a specified connection point and then advise or unadvise appropriately.
  • You can also use the ATL IDispEventImpl and IDispEventSimpleImpl classes and BEGIN_SINK_MAP, SINK_ENTRY, SINK_ENTRY_EX and END_SINK_MAP macros to implement an event sink. If you need to host the WebBrowser Control, you can use the ATL Object Wizard to create an HTML object.

Related Topics

The following articles provide information about the WebBrowser Control and MSHTML.

The following articles provide information about Visual Studio.

The following articles provide information about COM.

  • The Component Object Model Specification
  • Inside OLE, 2nd Edition, by Kraig Brockschmidt (Microsoft Press)
  • Understanding ActiveX and OLE, by David Chappell (Microsoft Press)
  • Inside COM, by Dale Rogerson (Microsoft Press)

Community Additions

ADD

Eventobject has not found when click in case of few pages

Hi,
First of all thanks for providing help.The above code works most of the time as expected.
But the it fails in case of few pages.
In such page, event is being successfully attached i.e. connect event method works.
But, I couldn’t found the element on which the event has being generating, once someone click(in my case)

Thanks, any help would really appreciate.

2/3/2012

ConnectEvents – unclear what pUnk is

It’s unclear as to how one would get the IUnknown pUnk in the ConnectEvents function (especially when using ATL to handle events ) ? How should the event sink be declared and initialized – for this purpose?

7/15/2009