diff --git a/src/dom/AttrImpl.cpp b/src/dom/AttrImpl.cpp
index 7e78cc4a691155ed113c68299cc2229cdcfbed76..0066c35718f9300591f9e2561a4206be24245d73 100644
--- a/src/dom/AttrImpl.cpp
+++ b/src/dom/AttrImpl.cpp
@@ -64,6 +64,7 @@
 #include "TextImpl.hpp"
 #include "ElementImpl.hpp"
 #include "DStringPool.hpp"
+#include "NodeIDMap.hpp"
 
 
 AttrImpl::AttrImpl(DocumentImpl *ownerDoc, const DOMString &aName) 
@@ -78,12 +79,15 @@ AttrImpl::AttrImpl(const AttrImpl &other, bool deep)
 {
     name = other.name.clone();
     specified(false);
+    idAttr(false);
     if (deep)
       cloneChildren(other);
 };
 
 
 AttrImpl::~AttrImpl() {
+    if (this->idAttr())
+        this->getOwnerDocument()->getNodeIDMap()->remove(this);
 };
 
 
@@ -177,6 +181,13 @@ void AttrImpl::setValue(const DOMString &val)
         );
     }
     
+    //  If this attribute was of type ID and in the map, take it out,
+    //    then put it back in with the new name.  For now, we don't worry
+    //    about what happens if the new name conflicts
+    //
+    if (idAttr())
+        this->getOwnerDocument()->getNodeIDMap()->remove(this);
+
     NodeImpl *kid;
     while ((kid = firstChild) != null)         // Remove existing kids
     {
@@ -189,6 +200,10 @@ void AttrImpl::setValue(const DOMString &val)
         appendChild(ownerDocument->createTextNode(val));
     specified(true);
     changed();
+    
+    if (idAttr())
+        this->getOwnerDocument()->getNodeIDMap()->add(this);
+
 };
 
 
diff --git a/src/dom/DocumentImpl.cpp b/src/dom/DocumentImpl.cpp
index f0e5047c40d6670111cf1fb6d3d6ebbd74dfb7b5..efca2a4a8c6bc86fc16550e808847b242595c9de 100644
--- a/src/dom/DocumentImpl.cpp
+++ b/src/dom/DocumentImpl.cpp
@@ -86,6 +86,7 @@
 #include <internal/XMLReader.hpp>
 #include "TreeWalkerImpl.hpp"
 #include "NodeIteratorImpl.hpp"
+#include "NodeIDMap.hpp"
 #include "DOM_Document.hpp"
 
 
@@ -97,6 +98,8 @@ DocumentImpl::DocumentImpl()
     namePool = new DStringPool(257);
     iterators = 0L;
     treeWalkers = 0L;
+    fNodeIDMap = 0;
+
 };
 
 
@@ -113,8 +116,9 @@ DocumentImpl::DocumentImpl(const DOMString &fNamespaceURI,
     docElement=null;
     appendChild(createElementNS(fNamespaceURI, qualifiedName));  //root element
     namePool = new DStringPool(257);
-    iterators = 0L;
-    treeWalkers = 0L;
+    iterators = 0;
+    treeWalkers = 0;
+    fNodeIDMap = 0;
 }
 
 void DocumentImpl::setDocumentType(DocumentTypeImpl *doctype)
@@ -158,6 +162,8 @@ DocumentImpl::~DocumentImpl()
     // Do not delete docType and docElement pointers here.
     //  These are also normal child nodes of the document,
     //  and refcounting will take them out in the usual way.
+
+    delete fNodeIDMap;
 };
 
 
@@ -651,7 +657,14 @@ DeepNodeListImpl *DocumentImpl::getElementsByTagNameNS(const DOMString &fNamespa
 
 ElementImpl *DocumentImpl::getElementById(const DOMString &elementId)
 {
-	return null;
+    if (fNodeIDMap == 0)
+        return null;
+
+    AttrImpl *theAttr = fNodeIDMap->find(elementId);
+    if (theAttr == null)
+	    return null;
+
+    return theAttr->getOwnerElement();
 }
 
 
diff --git a/src/dom/DocumentImpl.hpp b/src/dom/DocumentImpl.hpp
index 816a8dd60e77772cccdcc67bbbfd0be1ce90693a..e2f05684182363a43a53158d942eb57abb804cb9 100644
--- a/src/dom/DocumentImpl.hpp
+++ b/src/dom/DocumentImpl.hpp
@@ -98,6 +98,7 @@ class DOM_NodeFilter;
 class NodeFilterImpl;
 class DOM_DOMImplementation;
 class DOMString;
+class NodeIDMap;
 
 typedef RefVectorOf<NodeIteratorImpl> NodeIterators;
 typedef RefVectorOf<TreeWalkerImpl> TreeWalkers;
@@ -114,6 +115,7 @@ private:
     DocumentTypeImpl            *docType;
     ElementImpl                 *docElement;
     DStringPool                 *namePool;
+    NodeIDMap                   *fNodeIDMap;      // for use by GetElementsById().
 
     NodeIterators               *iterators;
     TreeWalkers                 *treeWalkers;
@@ -164,17 +166,19 @@ public:
     //Introduced in DOM Level 2
     virtual NodeImpl            *importNode(NodeImpl *source, bool deep);
     virtual ElementImpl         *createElementNS(const DOMString &namespaceURI,
-	const DOMString &qualifiedName);
+	                                             const DOMString &qualifiedName);
     virtual AttrImpl            *createAttributeNS(const DOMString &namespaceURI,
-	const DOMString &qualifiedName);
+	                                             const DOMString &qualifiedName);
     virtual DeepNodeListImpl    *getElementsByTagNameNS(const DOMString &namespaceURI,
-	const DOMString &localName);
+	                                              const DOMString &localName);
     virtual ElementImpl         *getElementById(const DOMString &elementId);
 
     //Return the index > 0 of ':' in the given qualified name qName="prefix:localName".
     //Return 0 if there is no ':', or -1 if qName is malformed such as ":abcd".
     static  int                 indexofQualifiedName(const DOMString & qName);
-    static  bool        isKidOK(NodeImpl *parent, NodeImpl *child);
+    static  bool                isKidOK(NodeImpl *parent, NodeImpl *child);
+
+    inline NodeIDMap *          getNodeIDMap() {return fNodeIDMap;};
 };
 
 #endif
diff --git a/src/dom/NodeIDMap.cpp b/src/dom/NodeIDMap.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..4b6f2eb6369517a4288db540c9394466ad2e25b1
--- /dev/null
+++ b/src/dom/NodeIDMap.cpp
@@ -0,0 +1,273 @@
+/*
+ * The Apache Software License, Version 1.1
+ * 
+ * Copyright (c) 1999-2000 The Apache Software Foundation.  All rights
+ * reserved.
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer. 
+ * 
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ * 
+ * 3. The end-user documentation included with the redistribution,
+ *    if any, must include the following acknowledgment:  
+ *       "This product includes software developed by the
+ *        Apache Software Foundation (http://www.apache.org/)."
+ *    Alternately, this acknowledgment may appear in the software itself,
+ *    if and wherever such third-party acknowledgments normally appear.
+ * 
+ * 4. The names "Xerces" and "Apache Software Foundation" must
+ *    not be used to endorse or promote products derived from this
+ *    software without prior written permission. For written 
+ *    permission, please contact apache\@apache.org.
+ * 
+ * 5. Products derived from this software may not be called "Apache",
+ *    nor may "Apache" appear in their name, without prior written
+ *    permission of the Apache Software Foundation.
+ * 
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * ====================================================================
+ * 
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation, and was
+ * originally based on software copyright (c) 1999, International
+ * Business Machines, Inc., http://www.ibm.com .  For more information
+ * on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ */
+
+#include "DOMString.hpp"
+#include "AttrImpl.hpp"
+#include "NodeIDMap.hpp"
+#include <util/XMLString.hpp>
+
+static const int gPrimes[] = {997, 9973, 99991, 999983, 0 };  // To do - add a few more.
+
+static const float gMaxFill = 0.8f;   // The maximum fraction of the total
+                                    // table entries to consume before exanding.
+
+NodeIDMap::NodeIDMap(int initialSize) 
+{
+    for (fSizeIndex = 0; gPrimes[fSizeIndex] < initialSize; fSizeIndex++);
+    {
+        if (gPrimes[fSizeIndex] == 0)
+        {
+            // We need a bigger size than the largest available one.
+            //   Big trouble.
+            fSizeIndex--;
+            throw "NodeIDMap::NodeIDMap - big trouble.";
+        }
+    }
+    
+    fSize = gPrimes[fSizeIndex];
+    fNumEntries = 0;
+    fMaxEntries = unsigned long(float(fSize) * gMaxFill);
+
+    fTable = new AttrImpl *[fSize];
+    unsigned int i;
+    for (i=0; i<fSize; i++)
+        fTable[i] = 0;
+};
+
+
+NodeIDMap::~NodeIDMap()
+{
+    delete[] fTable;
+    fTable = 0;
+};
+
+
+
+void NodeIDMap::add(AttrImpl *attr)
+{
+	//
+	//  If the table is getting too full, grow it.  We arbitrarily limit
+	//   the table to 80 full, which should limit the average number of
+	//   rehashes to a reasonable value.
+	//
+	if (fNumEntries >= fMaxEntries)
+		growTable();
+    fNumEntries++;
+
+	//
+	// Hash the value string from the ID attribute being added to the table
+	//      0 < Initial hash value < table size.
+	//      An initial hash of zero would cause the rehash to fail.
+	//
+	DOMString id=attr->getValue();
+	unsigned int initalHash = XMLString::hashN(id.rawBuffer(), id.length(), fSize-1);
+	initalHash++;
+	unsigned int currentHash = initalHash;
+
+	//
+	// Loop looking for an empty slot for this ID.
+	//   Don't even bother checking to see if the ID is already there - 
+	//   the table is only filled by the parser from valid documents, which
+	//   can not have duplicates.  Behavior of invalid docs is not defined.
+	//
+    while (true)
+	{
+		AttrImpl *tableSlot = fTable[currentHash];
+		if (tableSlot == 0 ||
+			tableSlot == (AttrImpl *)-1)
+			break;
+		currentHash += initalHash;  // rehash
+    }
+
+    //
+    // We've found our slot.  Stick the pointer to the attr into it.
+    //
+    fTable[currentHash] = attr;
+
+};
+
+
+void NodeIDMap::remove(AttrImpl *attr)
+{
+    //
+	// Hash the value string from the ID attribute being added to the table
+	//      0 < Initial hash value < table size.
+	//      An initial hash of zero would cause the rehash to fail.
+	//
+	DOMString id=attr->getValue();
+	unsigned int initalHash = XMLString::hashN(id.rawBuffer(), id.length(), fSize-1);
+	initalHash++;
+	unsigned int currentHash = initalHash;
+
+	//
+	// Loop looking for a slot pointing to an attr with this id.
+    //
+    while (true)
+	{
+		AttrImpl *tableSlot = fTable[currentHash];
+		if (tableSlot == 0)
+        {
+            // There is no matching entry in the table
+            return;
+        }
+
+        if (tableSlot == (AttrImpl *)-1)
+        {
+            // This slot contains an entry for a removed attribute.
+            //  We need to keep looking - the one we are after could
+            //  still show up later on.
+            continue;
+        }
+
+        if (tableSlot == attr)
+        {
+            //  Found the attribute.  Set the slot to -1 to indicate
+            //   that it was once used, meaning that lookups, while never
+            //   matching here, can not stop either, but must rehash again
+            //   and continue searching.
+            fTable[currentHash] = (AttrImpl *)-1;
+            return;
+        }
+
+        currentHash += initalHash;  // rehash.
+    }
+
+};
+
+
+AttrImpl *NodeIDMap::find(const DOMString &id)
+{
+    //
+    //  Get the hashcode for the supplied string.
+    //
+	unsigned int initalHash = XMLString::hashN(id.rawBuffer(), id.length(), fSize-1);
+	initalHash++;
+	unsigned int currentHash = initalHash;
+
+	//
+	// Loop looking for a slot pointing to an attr with this id.
+    //
+    while (true)
+	{
+		AttrImpl *tableSlot = fTable[currentHash];
+		if (tableSlot == 0)
+        {
+            // There is no matching entry in the table
+            return 0;
+        }
+
+        if (tableSlot == (AttrImpl *)-1)
+        {
+            // This slot contains an entry for a removed attribute.
+            //  We need to keep looking - the one we are after could
+            //  still show up later on.
+            continue;
+        }
+
+        if (tableSlot->getValue().equals(id))
+            return tableSlot;
+
+        currentHash += initalHash;  // rehash
+    }
+    return 0;  // Never gets here, but keeps some compilers happy.
+};
+
+
+//
+//  Grow the table to the next larger size.
+//      It has gotten too full for efficient operation.
+//     (We never fill it all the way)
+//
+void NodeIDMap::growTable()
+{
+    AttrImpl     **oldTable = fTable;
+    unsigned int oldSize  = fSize;
+
+    //
+    //  Figure the new table size.
+    //
+    fSizeIndex++;
+    unsigned int fSize = gPrimes[fSizeIndex];
+    if (fSize == 0)
+    {
+        // We need to grow bigger than the largest available size.
+        //   Big trouble.
+        fSizeIndex--;
+        throw "NodeIDMap::growTable - big trouble.";
+    }
+
+    //
+    //  Allocate the new table.
+    //
+    fTable = new AttrImpl *[fSize];
+    unsigned int i;
+    for (i=0; i<fSize; i++)
+        fTable[i] = 0;
+
+    fMaxEntries = unsigned long(float(fSize) * gMaxFill);
+
+    //
+    // Move entries over from the old table to the new one.
+    //
+    for (i=0; i<oldSize; i++)
+        add(oldTable[i]);
+
+    delete [] oldTable;
+
+};
+
+
+
diff --git a/src/dom/NodeIDMap.hpp b/src/dom/NodeIDMap.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..b79fcda4ea73fa7ccf6d27223457995d57987811
--- /dev/null
+++ b/src/dom/NodeIDMap.hpp
@@ -0,0 +1,120 @@
+#ifndef NodeIDMap_HEADER_GUARD_
+#define NodeIDMap_HEADER_GUARD_
+
+/*
+ * The Apache Software License, Version 1.1
+ * 
+ * Copyright (c) 1999-2000 The Apache Software Foundation.  All rights
+ * reserved.
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer. 
+ * 
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ * 
+ * 3. The end-user documentation included with the redistribution,
+ *    if any, must include the following acknowledgment:  
+ *       "This product includes software developed by the
+ *        Apache Software Foundation (http://www.apache.org/)."
+ *    Alternately, this acknowledgment may appear in the software itself,
+ *    if and wherever such third-party acknowledgments normally appear.
+ * 
+ * 4. The names "Xerces" and "Apache Software Foundation" must
+ *    not be used to endorse or promote products derived from this
+ *    software without prior written permission. For written 
+ *    permission, please contact apache\@apache.org.
+ * 
+ * 5. Products derived from this software may not be called "Apache",
+ *    nor may "Apache" appear in their name, without prior written
+ *    permission of the Apache Software Foundation.
+ * 
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * ====================================================================
+ * 
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation, and was
+ * originally based on software copyright (c) 1999, International
+ * Business Machines, Inc., http://www.ibm.com .  For more information
+ * on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ */
+
+
+//
+//  This file is part of the internal implementation of the C++ XML DOM.
+//  It should NOT be included or used directly by application programs.
+//
+//  Applications should include the file <dom/DOM.hpp> for the entire
+//  DOM API, or DOM_*.hpp for individual DOM classes, where the class
+//  name is substituded for the *.
+//
+
+
+
+
+//
+//  Class NodeIDMap is a hash table that is used in the implementation of
+//   of DOM_Document::getElementsByID().
+//
+//  Why Yet Another HashTable implementation?  Becuase it can be significantly
+//  smaller when tuned for this exact usage, and the generic RefHashTableOf
+//  from the xerces utils project is not a paricularly good fit.
+//
+class AttrImpl;
+class DOMString;
+
+
+class NodeIDMap {
+public:
+
+    NodeIDMap(int initialSize);    // Create a new hash table, sized to hold "initialSize"
+                                   //  Entries.  It will automatically grow if need be.
+
+    virtual ~NodeIDMap();
+
+private:
+    NodeIDMap(const NodeIDMap &other);   // No copy, assignement, comparison.
+    NodeIDMap &operator = (const NodeIDMap &other);
+    bool operator == (const NodeIDMap &other);
+
+public:
+    void  add(AttrImpl *attr);       // Add the specified attribute to the table.
+    void  remove(AttrImpl *other);   // Remove the specified attribute.
+                                           //   Does nothing if the node is not in the table.
+    AttrImpl *find(const DOMString &ID);   // Find the attribute node in the table with this ID
+
+private:
+    void growTable();
+
+private:
+    AttrImpl      **fTable;
+    unsigned int  fSizeIndex;              // Index of the current table size in the
+                                           //   array of possible table sizes.  
+	unsigned int  fSize;                   // The current size of the table array
+                                           //   (number of slots, not bytes.)
+    unsigned int  fNumEntries;             // The number of entries used.
+    unsigned int  fMaxEntries;             // The max number of entries to use before
+                                           //   growing the table.
+
+
+};
+
+#endif
diff --git a/src/dom/NodeImpl.cpp b/src/dom/NodeImpl.cpp
index e6df6e483cc1a3ba3412fe5ccd08f12c3eaa9353..c356252d9b90e54736cc9b49d036d5aacd1f7b89 100644
--- a/src/dom/NodeImpl.cpp
+++ b/src/dom/NodeImpl.cpp
@@ -85,6 +85,8 @@ const unsigned short NodeImpl::FIRSTCHILD   = 0x1<<4;
 const unsigned short NodeImpl::SPECIFIED    = 0x1<<5;
 const unsigned short NodeImpl::IGNORABLEWS  = 0x1<<6;
 const unsigned short NodeImpl::SETVALUE     = 0x1<<7;
+const unsigned short NodeImpl::ID_ATTR      = 0x1<<8;
+
 
 NodeImpl::NodeImpl(DocumentImpl *ownerDoc)
 {
diff --git a/src/dom/NodeImpl.hpp b/src/dom/NodeImpl.hpp
index 4f047b63ae9886fe1d84a2032e3e382d71f2fd7f..70157bb030fbe2fafee2e65023fb3ab4cb09c358 100644
--- a/src/dom/NodeImpl.hpp
+++ b/src/dom/NodeImpl.hpp
@@ -125,6 +125,7 @@ public:
     static const unsigned short SPECIFIED;
     static const unsigned short IGNORABLEWS;
     static const unsigned short SETVALUE;
+    static const unsigned short ID_ATTR;
 
     void *userData;
 
@@ -269,6 +270,15 @@ public: // should really be protected - ALH
     inline void setValue(bool value) {
         flags = (value ? flags | SETVALUE : flags & ~SETVALUE);
     }
+    
+    inline bool idAttr() {
+        return (flags & ID_ATTR) != 0;
+    }
+    
+    inline void idAttr(bool value) {
+        flags = (value ? flags | ID_ATTR : flags & ~ID_ATTR);
+    }
+    
 };
 
 
diff --git a/src/parsers/DOMParser.cpp b/src/parsers/DOMParser.cpp
index 6e907b77f8cfa6f819906609334042cead9f3de7..891b997a5985b812a26fe1ee6ca4cc5e3c24e2f0 100644
--- a/src/parsers/DOMParser.cpp
+++ b/src/parsers/DOMParser.cpp
@@ -84,6 +84,7 @@
 #include <dom/EntityImpl.hpp>
 #include <dom/NotationImpl.hpp>
 #include <dom/NamedNodeMapImpl.hpp>
+#include <dom/NodeIDMap.hpp>
 
 #include <validators/DTD/ContentSpecNode.hpp>
 
@@ -572,7 +573,9 @@ void DOMParser::startElement(const  XMLElementDecl&         elemDecl
                              , const bool                    isEmpty
                              , const bool                    isRoot)
 {
-    DOM_Element elem;
+    DOM_Element     elem;
+    DocumentImpl    *docImpl = (DocumentImpl *)fDocument.fImpl;
+
     if (fScanner -> getDoNamespaces()) {    //DOM Level 2, doNamespaces on
         unsigned int globalNSid = fValidator -> getGlobalNamespaceId();
         XMLBuffer buf;
@@ -598,11 +601,18 @@ void DOMParser::startElement(const  XMLElementDecl&         elemDecl
             }
             AttrImpl *attr = elemImpl->setAttributeNS(namespaceURI, oneAttrib -> getQName(),
             oneAttrib -> getValue());
-            // Register identifiers
+
+            // Attributes of type ID.  If this is one, add it to the hashtable of IDs
+            //   that is constructed for use by GetElementByID().
+            //
             if (oneAttrib->getType()==XMLAttDef::ID)
             {
-                // When we record ID attributes, here is the place to do it.
+                if (docImpl->fNodeIDMap == 0)
+                    docImpl->fNodeIDMap = new NodeIDMap(500);
+                docImpl->fNodeIDMap->add(attr);
+                attr->idAttr(true);
             }
+
             attr->setSpecified(oneAttrib->getSpecified());
         }
     } else {    //DOM Level 1
@@ -611,12 +621,18 @@ void DOMParser::startElement(const  XMLElementDecl&         elemDecl
         for (unsigned int index = 0; index < attrCount; ++index) {
             const XMLAttr* oneAttrib = attrList.elementAt(index);
             AttrImpl *attr = elemImpl->setAttribute(oneAttrib->getName(), oneAttrib->getValue());
-            // Register identifiers
+
+            // Attributes of type ID.  If this is one, add it to the hashtable of IDs
+            //   that is constructed for use by GetElementByID().
+            //
             if (oneAttrib->getType()==XMLAttDef::ID)
             {
-                // When we record ID attributes, here is the place to do it.
+                if (docImpl->fNodeIDMap == 0)
+                    docImpl->fNodeIDMap = new NodeIDMap(500);
+                docImpl->fNodeIDMap->add(attr);
+                attr->idAttr(true);
             }
-            attr->setSpecified(oneAttrib->getSpecified());
+
         }
     }