--- ---

Recently I got asked how to prevent an HTML document's background from being displayed in a TWebBrowser control. On investigation I found that there are four likely places where a HTML or XHTML document sets the background are:

1. From an external style sheet imported via the `<link>` tag or via an @import statement in a `<style>` tag.
2. From CSS code embedded in a `<style>` tag.
3. From a style attribute in the `<body>` tag.
4. From various deprecated attributes of the `<body>` tag.

To remove the background we need to scan a loaded HTML document, find the attributes and objects that define the background and reset their values. The styleSheets collection of the web browser's document object gives access to both the external and embedded style sheets. So that deals with cases 1 and 2 above. To deal with case 3 we need to find the document's body tag and access its style property. This can be found via the body tag's IHTMLElement interface. Finally, for case 4 we need to find the relevant attributes of the body tag. They are exposed by the background and bgColor properties of IHTMLBodyElement.

Starting with cases 1 and 2, the following code does what we want:

    procedure HandleStyleSheets(const Document: IDispatch);
    var
      Doc: IHTMLDocument2;                      // document object
      StyleSheets: IHTMLStyleSheetsCollection;  // document's style sheets
      SheetIdx: Integer;                        // loops thru style sheets
      OVSheetIdx: OleVariant;                   // index of a style sheet
      StyleSheet: IHTMLStyleSheet;              // reference to a style sheet
      OVStyleSheet: OleVariant;                 // variant ref to style sheet
      RuleIdx: Integer;                         // loops thru style sheet rules
      Style: IHTMLRuleStyle;                    // ref to rule's style
    begin
      // Get IHTMLDocument2 interface of document
      if not Supports(Document, IHTMLDocument2, Doc) then
        Exit;
      // Loop through all style sheets
      StyleSheets := Doc.styleSheets;
      for SheetIdx := 0 to Pred(StyleSheets.length) do
      begin
        OVSheetIdx := SheetIdx; // sheet index as variant required for next call
        // Get reference to style sheet (comes as variant which we convert to
        // interface reference)
        OVStyleSheet := StyleSheets.item(OVSheetIdx);
        if VarSupports(OVStyleSheet, IHTMLStyleSheet, StyleSheet) then
        begin
          // Loop through all rules within style a sheet
          for RuleIdx := 0 to Pred(StyleSheet.rules.length) do
          begin
            // Get style from a rule and reset required attributes.
            // Note: style is IHTMLRuleStyle, not IHTMLStyle, although many
            // attributes are shared between these interfaces
            Style := StyleSheet.rules.item(RuleIdx).style;
            Style.backgroundImage := '';  // removes any background image
            Style.backgroundColor := '';  // resets background colour to default
          end;
        end;
      end;
    end;

The comments hopefully explain but briefly, we loop through all the style sheets, and all the rules within each style sheet. We use the style property associated with each rule to access and reset the required style properties.

The next routine deals with case 3, the style attribute of the body tag. We simply grab the style property of the body tag's IHTMLElement interface.

    procedure HandleBodyStyleAttrs(const Document: IDispatch);
    var
      Doc: IHTMLDocument2;    // document object
      BodyElem: IHTMLElement; // reference to body element
      Style: IHTMLStyle;      // reference to body element's style attribute
    begin
      // Get document's IHTMLDocument2 interface
      if not Supports(Document, IHTMLDocument2, Doc) then
        Exit;
      // Get body tag's IHTMLElement interface
      if not Supports(Doc.body, IHTMLElement, BodyElem) then
        Exit;
      // Get style attribute of body element and reset required attributes
      Style := BodyElem.style;
      Style.backgroundImage := '';  // removes any background image
      Style.backgroundColor := '';  // resets background colour to default
    end;

The last routine deals with case 4, the deprecated attributes of the body tag. This time instead of the IHTMLElement interface of the body tag we use its IHTMLBodyElement interface to access the relevant attributes.

    procedure HandleBodyAttrs(const Document: IDispatch);
    var
      Doc: IHTMLDocument2;        // document object
      BodyElem: IHTMLBodyElement; // reference to body element
    begin
      // Get document's IHTMLDocument2 interface
      if not Supports(Document, IHTMLDocument2, Doc) then
        Exit;
      // Get body tag's IHTMLBodyElement interface
      if not Supports(Doc.body, IHTMLBodyElement, BodyElem) then
        Exit;
      // Reset required deprecated attributes of body tag
      BodyElem.background := '';  // removes any background image
      BodyElem.bgColor := '';     // resets background colour to default
    end;

To use these routines you should load a new document into the web browser control, wait for the document to load then call each of the routines. Any background should hopefully be removed. Here's some example code that loads a HTML document named `Test.html` into a TWebBrowser named WebBrowser1:

    begin
      WebBrowser1.Navigate('Test.html');
      while WebBrowser1.ReadyState <> READYSTATE_COMPLETE do
        Application.ProcessMessages;
      HandleStyleSheets(WebBrowser1.Document);
      HandleBodyStyleAttrs(WebBrowser1.Document);
      HandleBodyAttrs(WebBrowser1.Document);
    end;

## Demo code

A ready made project containing this demo code is available. View the project.

This demo loads a selected website into a browser control and then uses the routines presented in this tip to remove any background colours.

Start a new Delphi VCL application. Drop TWebBrowser, TEdit and TButton controls onto the form. Create an OnClick event handler for the button. Name the form "Form1" and save the unit as `Unit1.pas`. Now code Unit1 as follows:

    unit Unit1;

    interface

    uses
      SHDocVw, StdCtrls, Classes, Controls, OleCtrls, Forms;

    type
      TForm1 = class(TForm)
        WebBrowser1: TWebBrowser;
        Edit1: TEdit;
        Button1: TButton;
        procedure Button1Click(Sender: TObject);
      end;

    var
      Form1: TForm1;

    implementation

    uses
      SysUtils, Variants, Windows, MSHTML;

    {$R *.dfm}

    procedure HandleStyleSheets(const Document: IDispatch);
    var
      Doc: IHTMLDocument2;
      StyleSheets: IHTMLStyleSheetsCollection;
      SheetIdx: Integer;
      OVSheetIdx: OleVariant;
      StyleSheet: IHTMLStyleSheet;
      OVStyleSheet: OleVariant;
      RuleIdx: Integer;
      Style: IHTMLRuleStyle;
    begin
      if not Supports(Document, IHTMLDocument2, Doc) then
        Exit;
      StyleSheets := Doc.styleSheets;
      for SheetIdx := 0 to Pred(StyleSheets.length) do
      begin
        OVSheetIdx := SheetIdx;
        OVStyleSheet := StyleSheets.item(OVSheetIdx);
        if VarSupports(OVStyleSheet, IHTMLStyleSheet, StyleSheet) then
        begin
          for RuleIdx := 0 to Pred(StyleSheet.rules.length) do
          begin
            Style := StyleSheet.rules.item(RuleIdx).style;
            Style.backgroundImage := '';
            Style.backgroundColor := '';
          end;
        end;
      end;
    end;

    procedure HandleBodyStyleAttrs(const Document: IDispatch);
    var
      Doc: IHTMLDocument2;
      BodyElem: IHTMLElement;
      Style: IHTMLStyle;
    begin
      if not Supports(Document, IHTMLDocument2, Doc) then
        Exit;
      if not Supports(Doc.body, IHTMLElement, BodyElem) then
        Exit;
      Style := BodyElem.style;
      Style.backgroundImage := '';
      Style.backgroundColor := '';
    end;

    procedure HandleBodyAttrs(const Document: IDispatch);
    var
      Doc: IHTMLDocument2;
      BodyElem: IHTMLBodyElement;
    begin
      if not Supports(Document, IHTMLDocument2, Doc) then
        Exit;
      if not Supports(Doc.body, IHTMLBodyElement, BodyElem) then
        Exit;
      BodyElem.background := '';
      BodyElem.bgColor := '';
    end;

    procedure TForm1.Button1Click(Sender: TObject);

      // from https://delphidabbler.com/tips/72
      procedure Pause(ADelay: Cardinal);
      var
        StartTC: Cardinal;
      begin
        StartTC := GetTickCount;
        repeat
          Application.ProcessMessages;
        until Int64(GetTickCount) - Int64(StartTC) >= ADelay;
      end;

    begin
      WebBrowser1.Navigate(Edit1.Text);
      while WebBrowser1.ReadyState <> READYSTATE_COMPLETE do
        Pause(5);
      HandleStyleSheets(WebBrowser1.Document);
      HandleBodyStyleAttrs(WebBrowser1.Document);
      HandleBodyAttrs(WebBrowser1.Document);
    end;

    end.

Run the application. Enter a web page address in the edit box and click the button. The web page will be displayed in the browser control. When the web page has finished loading the background colours should be removed. Note there may be quite a delay before this happens.

All that happens in the button click event is that the web page is loaded and a loop is entered waiting for the web document to finish loading. Then the three routines presented in this tip are called to try to remove the background. Once this is done the browser re-displays the web page.

## Going further

In addition to the background you may also want to reset the body text colour to the browser default. To do this set the color properties of IHTMLRuleStyle and IHTMLStyle and the text property of IHTMLBodyElement to the empty string.

You can also use these techniques to force a required background, colours, margins etc.