Yule Not Want to Miss this One

As of December 2011, this topic has been archived. As a result, it is no longer actively maintained. For more information, see Archived Content. For information, recommendations, and guidance regarding the current version of Internet Explorer, see Internet Explorer Developer Center.

Jay Allen, Mark Davis, Heidi Housten, and Dan Mohr
Microsoft Corporation

December 11, 2001

'Twas a dark and dismal December day in Seattle and Stockholm, so we started comparing notes on what makes this month bright and exciting. We certainly don't have any recollections of the Decembers of our childhoods being so dreary! We snickered when Heidi said that in Sweden the tradition used to be that on Christmas Eve a goat would come knocking at the door, throw presents in, shout something rude, and leave. On reflection, we realized that the United States version of flying reindeer and chimney hopping was even more farfetched. The Swedish tradition of making up rhyming riddles to describe the contents of a gift appealed to us, so we put our heads together to think up a rhyme about what we'd all like for the holidays—after peace and joy to all of course!

We've been good all the year,

     And don't expect any rocks,

From the man with the deer.

     If we found this under our socks

We'd shed not a tear;

     Our friends would come visit in flocks!

With a whoop and a cheer,

     We'd plug in our Xbox!

Contents

Delivering Data Behind the Scenes—DHTML behavior at work
O Cursor, Where Art Thou—finding yourself in a text box
Searching for Buried Tags—hidden resources

Web Team in Short

Delivering Data Behind the Scenes

Dear Web Team:

How do I use a DHTML behavior to encapsulate database access? The database will be accessed from an ASP page and I don't want to refresh the Web page the user is currently viewing.

Osiris

The Web Team replies:

Dynamic HTML (DHTML) behaviors are a client-side Internet Explorer technology that can be used to encapsulate common script and user interface elements. There are several ways that you could use a DHTML behavior to encapsulate your data access. We'll briefly outline some choices and provide a neat solution to your problem.

You've already mentioned that you are using Active Server Pages (ASP) to access the data, and we'll assume that you're using ActiveX® Data Objects (ADO) to access a SQL Server database. The next decision you'll want to make is the format of the data you want to send to the client. The two ideal choices would be HTML and XML. Both of these involve a similar amount of data being transmitted, with the HTML using a little more server processing than the XML. We suggest that you use XML for two reasons. One, your behavior can be more flexible if you allow the HTML representation of your data to be decided on the client. Two, ADO provides a fast and simple way to save a recordset as XML, saving precious server cycles. Remember to set the response content type to text/xml when returning XML data to the client. The following ASP code demonstrates how you could use ADO to retrieve a recordset from the pubs database, and then save it to the HTTP response stream as XML.

<%@ Language=JScript %>
<%

  Response.ContentType = "text/xml";
  var conn = Server.CreateObject( "ADODB.Connection" );
  conn.ConnectionString = "Driver={SQL Server};
    Server=(local);Database=pubs;Uid=sa;Pwd=";
  // Connect to database
  conn.Open();
  // Run SQL query
  var rs = conn.Execute( "select * from titles" );
  // Save recordset as XML
  rs.Save( Response, 1 ); // 2nd argument is adPersistXML
  conn.Close();
%>

You also mention that you don't want to display another window or refresh the Web page to get your data. An alternative to navigating to a Web page is to use the XMLHttpRequest object. This object enables your client script to perform an HTTP request and return the resulting data as a string. Your behavior can then process the XML and build the custom element's HTML content that will be displayed in the Web page. The sample below demonstrates script to process the XML, but you could easily use an XSLT stylesheet for a neater way of transforming the XML into HTML.

<PUBLIC:COMPONENT tagName="datatable">
  <PUBLIC:DEFAULTS viewLinkContent/>
  <PUBLIC:PROPERTY NAME="xmlurl" PUT="putXML"/>
</PUBLIC:COMPONENT>
<SCRIPT LANGUAGE="JScript">
var oXML = new ActiveXObject( "Microsoft.XMLDOM" );
oXML.async = false;
function putXML(xmlurl)
{
  // Request data
  var x = new ActiveXObject( "Microsoft.XMLHTTP" );
  x.open( "GET", xmlurl, false );
  x.send();
  // Transform XML to HTML
  transformXML( x.responseXML.xml );
}
function transformXML( xml )
{
var str = "<TABLE BORDER=1 STYLE='border-collapse:collapse'>";
var aColNames = new Array();
var iColNames;
var sData;
var oNodes;
var iNodes;

  oXML.loadXML( xml );

  // Use attribute names to display table header
  oNodes = oXML.selectNodes( "xml/s:Schema/s:ElementType/s:AttributeType" );
  iNodes = oNodes.length;
  for ( var i=0; i<iNodes; i++ )
  {
    sData = oNodes[i].getAttribute( "name" );
    aColNames[aColNames.length] = sData;
    str += "<TH>" + sData + "</TH>";
  }
  iColNames = aColNames.length;

  // Display each row
  oNodes = oXML.selectNodes( "xml/rs:data/z:row" );
  iNodes = oNodes.length;
  for ( i=0; i<iNodes; i++ )
  {
    str += "<TR>";
    for ( var j=0; j<iColNames; j++ )
    {
      sData = oNodes[i].getAttribute( aColNames[j] );
      if ( sData == null ) sData = "";
      str += "<TD>" + sData + "</TD>";
    }
    str += "</TR>";
  }

  str += "</TR></TABLE>";
  document.body.innerHTML = str;
}
</SCRIPT>
<BODY>
</BODY>

The following HTML shows how to use the DHTML element behavior to display the data. This simple behavior will display a table providing the specified Web page returns an ADO recordset persisted as XML. We'll leave it to you to add further properties that could specify an XSLT stylesheet or display type (grid or list, for example).

It might be tempting to add the SQL query as a property, but beware that it's a security risk to allow users to process arbitrary SQL commands on your database—this is one reason you want to keep all your data access logic on the server.

<HTML xmlns:db>
<HEAD>
<?IMPORT NAMESPACE="db" IMPLEMENTATION="datatable.htc">
</HEAD>
<BODY>
<db:datatable xmlurl="http://myserver/mydata.asp"/>
</BODY>
</HTML>

We can't leave you without mentioning some other technologies that are just perfect for your scenario. First, you might want to consider implementing your data layer as an XML Web service. On the client side, you can make use of the Internet Explorer WebService behavior to invoke your Web methods, returning an ADO dataset if required. Second, if this is an intranet solution, SQL Server 2000 provides some exciting features that allow you to return the results of a SQL query as XML, avoiding the need to convert a recordset into XML—good news for server performance. When using XML for SQL Server, you can provide the SQL statement as part of a URL, so this works very well when using the XMLHttpRequest object in your client script.

O Cursor, Where Art Thou?

Dear Web Team:

I'm trying to find out how to get the current cursor position in a text input field.

Rodney

The Web Team replies:

It can be useful to know where the user cursor, or caret, is positioned. Here are some features that might make use of this information:

  • Moving the cursor to the next field once the required characters have been entered, such as the area code for a telephone number.
  • Providing 'as you type' field validation, such as limiting the number of digits in an account number.
  • Providing automatic formatting or word completion.
  • Inserting boiler template text.

There is no direct property or method in the DHTML object model that will provide the character position of the cursor in a text element. What we're going to do is create a TextRange object that represents the cursor position. To do this, you need to obtain the current text selection from the selection property of the document object. Then, call the createRange() method on the selection object. If no text has been selected, the resulting TextRange object will contain no text, but will represent the current cursor position. If you intend to insert text at the current location, you can use the TextRange object by setting the text property to the required text. Remember, if text is selected, then this will be overwritten. The TextRange object also has a pasteHTML() method if you need to set richer content.

In the sample HTML, the TextRange object, representing the cursor location, is saved to an expando property whenever the user types, clicks or selects the text element. The reason for this is that when the user clicks the button, the selection is removed, so this may not be necessary if you are directly handling the onkeyup event, for example. Also, notice that the code is checking the isTextEdit property that indicates whether a TextRange object can be created for an element.

The TextRange object can be used to manipulate the underlying text, and can act as a bookmark for the current position, but cannot tell us the character position. The workaround we use is to insert a unique character, or set of characters, at the current location and use the JScript search() function to find this text. Once we have the character position, we can replace the text with the original value.

Here is some example HTML and script that demonstrates the technique described:

<HTML>
<HEAD>
<SCRIPT LANGUAGE="JScript">
function saveCaret(elem)
{
  if ( elem.isTextEdit ) 
    elem.caretPos = document.selection.createRange();
}
function getCaretPos(elem)
{
  if ( elem.isTextEdit && elem.caretPos )
  {
    var bookmark = "~";
    var orig = elem.value;
    var caretPos = elem.caretPos;
    caretPos.text = bookmark;
    var i = elem.value.search( bookmark );
    window.status = "Caret is at character " + i;
    elem.value = orig;
  }
}
</SCRIPT>
</HEAD>

<BODY>
<INPUT NAME="txtInput" ONSELECT="saveCaret(this)" 
   ONCLICK="saveCaret(this)" ONKEYUP="saveCaret(this)" VALUE="Where are you?">
<INPUT TYPE="button" VALUE="caret pos" ONCLICK="getCaretPos(txtInput)">
</BODY>
</HTML>

Searching for Buried Tags

Dear Web Team:

I'm re-using IE [Internet Explorer] in a C++ app ( using the ATL ). I'd like to load a DHTML document from a resource buried in the exe, rather than from a disk file so I can hide it from casual users. I've been able to load the HTML by:

  1. Navigating to about:blank
  2. Retrieving an IHTMLDocument2 interface to the blank document
  3. Using IHTMLDocument2::write and close.

One problem: The DHTML doesn't work. This process of loading the document seems to bypass the scripting engine. Is there another way?

Tim Altonji

The Web Team Responds:

In theory, Tim, this should work fine—and does, if you're hosting the WebBrowser control. The following code in Visual Basic® (which we use here for simplicity) works well in Internet Explorer 6.0:

Private Sub Form_Load()
    WebBrowser1.Navigate "about:blank"
End Sub

Private Sub WebBrowser1_DocumentComplete(ByVal pDisp As Object, URL As Variant)
    WebBrowser1.Document.open
    WebBrowser1.Document.write "<SCRIPT>window.alert(""Hello, world!"");</SCRIPT>"
    WebBrowser1.Document.Close
End Sub 

One reason this might not work is if you're hosting MSHTML directly. Most of our samples of MSHTML hosting, such as the famous WalkAll sample, turn off script execution by responding to the DISPID_AMBIENT_DLCONTROL dispatch property call with the DLCTL_NO_SCRIPTS bitmask, as discussed in WebBrowser Customization. As discussed in Knowledge Base article Q266343, it is not advised to turn script handling on when hosting MSHTML as a UI-less parser. If you're using someone else's framework code to host MSHTML, make sure they didn't deactivate scripting on your behalf.

There's also an alternative method to load HTML from a resource—the RES protocol. RES is a pluggable protocol built to load RT_HTML resource types from any Portable Executable file in the system. In Visual C++&reg; 6 or Visual C++ 7, just create a simple ATL project and use the Add Resource… menu command from the Project menu to add an HTML page. Visual C++ will automatically assign a numeric ID to your resource. Next, Compile the DLL and place it somewhere on your system's DLL search path (%SYSTEMDIR%\system32 is a prime candidate). If your DLL is named SomeLibrary.dll, and your HTML page is resource 201, you can then load it easily with a call to Navigate():

WebBrowser1.Navigate "res://SomeLibrary.dll/201"

Be warned that using RES will have its disadvantages:

  • Code will not be hidden from casual users (unless you put all your script into files also stored in the PE).
  • Per Knowledge Base article Q272762, you can't load licensed controls in an HTML file loaded using RES.
  • At least some (if not all) versions of the MSXML parser will not allow you to load XML or XSL files through RES. The parser relies on the existence of a cache file for its resources, and RES creates no entries in the cache.
  • If you mix and match content from RES and HTTP protocols, you're liable to run afoul of cross-frames scripting security. Internet Explorer does not allow two FRAMEs or IFRAMEs to script one another if their security contexts—the conjunction of protocol, domain, and zone—do not match.

Web Team in Short

File:// Parameters

Q: George wants to know how to pass parameters to an HTML file on a client machine using the file:// protocol.

A: You can use the document.location.search property and parse the parameter string yourself. To pass some parameters to a file c:\blah.htm, save the following code as c:\blah.htm and navigate Internet Explorer to file:///C:/blah.htm?name=marvin&occupation=martian. This should get you started:

<HTML>
<BODY ONLOAD="body_onLoad();">
<SCRIPT LANGUAGE="JSCript">
function body_onLoad()
{
   var sSearch;
   var aPairs;
   var i;
   sSearch = (document.location.search.length > 1) ? document.location.search.substring(1) : "";
   if (sSearch != "")
   {
      aPairs = sSearch.split("&");
      for (i = 0; i < aPairs.length; i++)
      {
         alert("Paramter " + i + ": Name=" + aPairs[i].split("=")[0] + "  
            Value=" + aPairs[i].split("=")[1]);
      }
   }
}
</SCRIPT>
<P>Hello, world.</P>
</BODY>
</HTML>

Closing Down or Leaving?

Q: Ikram wants to know if you can detect when the user closes the browser from script.

A: The short answer is no, but since we've got a little extra room, there is a way to make a very educated guess. If you use the following as your BODY onUnload event handler, you'll catch it in virtually all cases:

function body_onUnload()
{
   if (window.event.clientX < 0)
   {
      alert("The browser is closing...");
   }
   else
   {
      alert("The user is refreshing or navigating away...");
   }
}

Do Not Enter

Q: Brijesh has a form with just a single text field and wants to prevent Enter from submitting the form (which is the default behavior).

A: Here's how it's done—just add an onKeyPress event handler to the textbox like so:

<INPUT NAME="txtBlah" TYPE="TEXT" onkeypress="if 
   (window.event.keyCode==13) { window.event.returnValue=false; }">

The Web Team

Mark Davis is a software design engineer on the Internet Explorer SDK team. Mark originates from England and is currently training to climb the major summits in the Northwest.

Heidi Housten works as a Consultant with Microsoft Consulting Services in Sweden after spending some time in Developer Support and MSDN. It is only a rumor that she moved there to escape the drizzle of Seattle; she really went for the traditional crayfish parties in August.

Dan Mohr, an engineer with Microsoft Developer Support's Internet Client team, spends his free minutes recording bands in his basement, programming his Commodore 64, and extolling the virtues of late '70s punk rock.

Jay Allen, a Support Engineer for the Internet Client team in Microsoft Developer Support, longs for the integration of Notepad and Emacs Lisp. What little time is not consumed by his four children is usually spent reading math books, studying Japanese and programming in Haskell.


The Web Team's Greatest Hits

List of Web Team Topics


  
Show: