How To Navigate to an Anchor in the Microsoft WebBrowser Control when Rendering HTML from Memory

In my previous blog entry titled “How To Use the Microsoft WebBrowser Control to Render HTML from Memory” I described a method how you could use the Microsoft WebBrowser Control to display HTML from memory. One commenter said that it was not possible to navigate to an anchor in the body onload handler. I did some research and it seems all navigation within the rendered page is not working. For example, the following piece of HTML code will not work correctly:

<a href="#n25">Jump to anchor n25</a>
<a name="n25">25</a>

It took me a while to find a workaround, so that’s why I’m posting it now for other people to use. Basically, we cannot use the standard navigation techniques. I tried several possible workaround and the only one that I got working properly is by manually scrolling the window until the requested anchor is visible. It sounds complicated, but it really works pretty nicely. I wrote this little wrapper function to do all the hard work.

void CMyDlg::ScrollToAnchor(const wchar_t* anchor)
{
    IDispatch* pHtmlDoc = m_explorer.get_Document();
    if (!pHtmlDoc)
        return;
    CComPtr<IHTMLDocument2> doc2;
    doc2.Attach((IHTMLDocument2*)pHtmlDoc);
    if (doc2)
    {
        CComPtr<IHTMLElementCollection> anchors;
        HRESULT hr = doc2->get_anchors(&anchors);
        if (SUCCEEDED(hr) && anchors)
        {
            _variant_t index = 0;
            _variant_t str = anchor;
            IDispatch *pdisp;
            hr = anchors->item(str, index, &pdisp);
            if (SUCCEEDED(hr) && pdisp)
            {
                CComPtr<IHTMLElement> el;
                hr = pdisp->QueryInterface(IID_IHTMLElement, (void**)&el);
                if (SUCCEEDED(hr) && el)
                {
                    long yTotal = 0;
                    while (1)
                    {
                        long y;
                        el->get_offsetTop(&y);
                        yTotal += y;
                        CComPtr<IHTMLElement> el2;
                        hr = el->get_offsetParent(&el2);
                        if (SUCCEEDED(hr) && el2)
                            el = el2;
                        else
                            break;
                    }
                    CComPtr<IHTMLWindow2> wnd;
                    hr = doc2->get_parentWindow(&wnd);
                    if (SUCCEEDED(hr) && wnd)
                        wnd->scrollTo(0, yTotal);
                }
            }
        }
    }
}

What it does is it gets a pointer to the document. Then gets a list of all the anchors in the document and get the anchor with the given name out of that list. Once we have the target element, we calculate the offset from the top of the document. This is done in a while loop, because the target anchor could be inside another element like a div or a table. After calculating the offset, we get a pointer to the HTML window and call the scrollTo function to make it scroll to the anchor position.

Now the only thing you need to do is to render your HTML using the method in my previous blog entry and then call this new ScrollToAnchor function with the name of the anchor to which you want to scroll.

Share

Leave a Comment

Name: (Required)

E-mail: (Required)

Website:

Comment: