Introduction
Style Inspector is a simple tool tailored to current needs of people working in teams to develop web pages or HTML based documentation, teams within which brilliant people use daily their native talent and knowledge to achieve the tasks. Frequently, documents make use of "in-line" style sheets while plenty of style sheets are referenced by a bunch of multiple pages. All of a sudden, a team member decides to use some style elements on individual items within the page he is working on. "At the end of the day", the command closest to the element will be the one to affect the look of the document. To keep the whole work professional, there should be one (or more) members of the team dealing with checking the results and making sure the outcome fits to the target(s).
Last but not least, there are the people learning HTML and willing to use CSS to control their pages. One of the ways to do it is by looking at how others are dealing with that. I bet those people would appreciate a tool allowing them to "go" over an HTML page and "see" what is happening beyond the scene.
Using the Style Inspector
Style Inspector consists mainly of two windows: the info window and the hit window, both created with the "top most" flag set, so that they stay always on top of the other windows on your screen.
The info window keeps track of the opened browser windows. The URL used of each browser instance to display an HTML content is listed within this window as soon as the loading process is ended.
If only one browser window is present (meaning, you get only one item in the info list), the document displayed by it is automatically selected. Otherwise, you have to choose an item from the info list in order to enable the actual use of the hit window.
You should pay attention to one important thing: the info list refers the different instances of your internet browser and not the displayed pages. So, should you navigate with your browser, you must click on the "Update" button of the hit window in order to inspect the latest document(s).
Once you have selected an item from the info list, the "Origin" button of the hit window turns active and you may start inspecting the loaded document.
Grab the upper half of the diamond within the hit window (that is actually part of the title bar of the window allowing you to drag the window over the screen) and move the hit window so that its top-left corner points to the top-left corner of the "view" area of your browser. Click now the "Origin" button.
From now on, the current "hit" position within the browser display area is shown at the bottom of the hit window. The yellow pointer (the icon of the hit window) will point to an HTML element while moving the hit window around. The type of the hovered element (the HTML tag) is displayed in the info window, along with its position, size and style.
Individual HTML elements may be affected by in-line style elements, rather than spanned styles. In that case, the information related to the style is referred as "local" within the info window. Only inspection for color and font is supported.
You may remark differences between the hit position and the position displayed for an HTML element while inspecting the page. That is entirely OK! The hit point is relative to the top-left corner of the "view" area of your browser, while an HTML element position is computed relative to the top-left corner of the entire document (document which can be scrolled within the browser). Just do not forget to set again the origin whenever the browser's position (and not the document scroll) changes on the screen.
Note: you should also repeat the process of setting the origin when you customize the appearance of your browser (this also affects the position of the "view" area on the screen).
How this works
Note: The code blocks within the article are simplified and some of the variable names are adjusted for a better understanding of the explanation, when compared to the source code.
At start, Style Inspector makes use of a SHDocVw::IShellWindowsPtr m_pInterface
pointer to create an instance of a ShellWindows connectable object. In case of success, it "asks" the object about its outgoing interface by using QueryInterface()
on IID_IConnectionPointContainer
. If the object supports the outgoing interface, the application gets a valid pointer to the connection point container. Read more on how to use connectable objects on the help and support site of Microsoft. With that pointer, it finds the specific event sink interface and retrieves a pointer to the connection point object. In case of success, it establishes the connection between the sink and the connection point object, and retrieves a cookie value for terminating the connection later. At this point, Style Inspector creates a list of open browser windows and fills the list of corresponding document URLs within the info window, using its specially created function UpdateListOfWindows()
.
for (long i=0; i<m_pInterface->GetCount(); i++)
{
_variant_t va(i, VT_I4);
IDispatchPtr lpDisp = m_pInterface->Item(va);
SHDocVw::IWebBrowser2Ptr lpBrowser(lpDisp);
if ( lpBrowser != NULL )
{
.....
.....
.....
.....
.....
lpBrowser->AddRef();
}
}
If the resulted list of URLs contains only one item, that item is automatically selected within the list, a boolean m_bPageIsLoadedAndSelected
flag is set to FALSE
, and Style Inspector starts polling the busy status of the selected browser using a 100 ms timer (otherwise, the user must make a selection). As long as the browser is busy finding and loading the document, the "Origin" button of the hit window stays disabled.
The UpdateListOfWindows()
function is called each time a browser window instance is registered or revoked, through the following dispatch mechanism, within the IsRegistered()
and IsRevoked()
functions respectively:
BEGIN_DISPATCH_MAP(CStyleInspectorDlg, CCmdTarget)
DISP_FUNCTION_ID(CStyleInspectorDlg, "WindowRegistered",
0x000000C8, IsRegistered, VT_EMPTY,VTS_I4)
DISP_FUNCTION_ID(CStyleInspectorDlg, "WindowRevoked",
0x000000C9, IsRevoked, VT_EMPTY,VTS_I4)
END_DISPATCH_MAP()
Once the browser is no longer busy, the "Origin" button turns active and the user can perform the process of setting the origin as described earlier in this article. Behind the scene, Style Inspector collects all the information on style(s) for the loaded document before setting the m_bPageIsLoadedAndSelected
flag to true. Assuming IWebBrowser2 *lpSelBrowser
to be the pointer to the currently selected browser instance and lpDispatch
a pointer to the IDispatch
interface, Style Inspector obtains a pointer to the loaded document:
lpSelBrowser->get_Document(&lpDispatch);
lpDispatch->QueryInterface(IID_IHTMLDocument2, (void**)&lpHtmlDocument);
and calls the FindStyleClasses()
function:
if ( lpHtmlDocument ) FindStyleClasses(lpHtmlDocument);
where lpHtmlDocument
is of IHTMLDocument2*
type. Within this function, it uses the lpHtmlDocument->get_styleSheets()
function to retrieve a pointer to the collection of elements used by the document and then iterates through that collection:
IHTMLStyleSheetsCollection *pElemCollection;
IUnknown *lpUnknown;
IEnumVARIANT *lpNewEnum;
IHTMLStyleSheet *lpElement;
VARIANT varElement;
BSTR bstrStyleSheet;
CString strStyle;
if ( lpHtmlDocument->get_styleSheets(&pElemCollection) != S_OK ) return;
if ( pElemCollection == NULL ) return;
if ( !m_listElements.IsEmpty() ) m_listElements.RemoveAll();
if (SUCCEEDED(pElemCollection->get__newEnum(&lpUnknown)) && lpUnknown != NULL)
{
lpUnknown->QueryInterface(IID_IEnumVARIANT,(void**)&lpNewEnum);
if (lpNewEnum == NULL) return;
while (lpNewEnum->Next(1, &varElement, NULL) == S_OK)
{
if (varElement.vt != VT_DISPATCH) { VariantClear(&varElement); break; }
varElement.pdispVal->QueryInterface(IID_IHTMLStyleSheet,(void**)&lpElement);
if (lpElement)
{
lpElement->get_cssText(&bstrStyleSheet);
_bstr_t bstr1(bstrStyleSheet);
strStyle.Format(_T("%s"), (LPCTSTR)bstr1);
m_listElements.AddTail(strStyle);
}
VariantClear(&varElement);
}
if ( lpNewEnum ) lpNewEnum->Release();
}
The style inspection process is entirely achieved within the OnMove()
function of the hit window. Having the screen coordinates of the hit window obtained through the GetWindowRect()
function and the offset values on x and y axes obtained in the process of setting the origin, Style Inspector obtains a pointer to the HTML element under the top-left corner of the hit window through:
lpHtmlDocument->elementFromPoint(xPos, yPos, &lpElement)
where lpElement
is of IHTMLElement*
type. Most of the time, an HTML element which a user sees is contained within another, and the containment may occur multiple times. That is why the left, top, width and height values of element pointed to by lpElement
may not represent its absolute position (considered from the top-left corner of the document).
Let lLeft
, lTop
, lWidth
and lHeight
be the geometrical elements of the element pointed to by lpElement
. A block of code like the following one helps getting the real values:
IHTMLElement *lpElement, *lpContainer;
long lValue, lTop, lLeft, lWidth, lHeight;
lpElement->get_offsetTop(&lValue); lTop = lValue;
lpElement->get_offsetLeft(&lValue); lLeft = lValue;
lpElement->get_offsetWidth(&lValue); lWidth = lValue;
lpElement->get_offsetHeight(&lValue); lHeight = lValue;
lpElement->get_offsetParent(&lpContainer);
while ( lpContainer )
{
lpContainer->get_offsetLeft(&lValue); lLeft += lValue;
lpContainer->get_offsetTop(&lValue); lTop += lValue;
lpContainer->get_offsetParent(&lpContainer);
}
Calling the lpElement->get_tagName()
and lpElement->get_className()
functions, Style Inspector retrieves the tag name of the HTML element and the particular style rule (class) within the style sheet associated with it, respectively. Then, Style Inspector "looks" for that class within the list of style elements it built before setting the m_bPageIsLoadedAndSelected
flag to true, and displays the style information within the info window. After that, Style Inspector checks for the presence of local (or in-line) style elements and displays them if any:
IHTMLStyle *pStyle;
CString strInfo;
VARIANT vtValue;
lpElement->get_style(&pStyle);
if ( pStyle )
{
if ( pStyle->get_color(&vtValue) == S_OK )
{
_bstr_t bstrA(vtValue.bstrVal);
strInfo.Format(_T(", local color:%s"), (LPCTSTR)bstrA);
}
if ( pStyle->get_font(&bstrInfo) == S_OK )
{
_bstr_t bstrB(bstrInfo);
strInfo.Format(_T(", local font:%s"), (LPCTSTR)bstrB);
}
}
Instead of conclusions
Before being "cool" or not, a tool must be a tool. That means, a tool should provide you the means to perform what you need to.
Joke: Why has C++ pluses and VB not? Because C++ has something more: the local color is black :-))
History
- Posted: Dec. 10, 2004.
- Section "How this works" added: Dec. 12, 2004.