Link to home
Start Free TrialLog in
Avatar of moshem
moshem

asked on

Injecting HTML to exisitng HTML using C++ and MSHTML

Hi,

I recently asked a question on how to embeed an html control in my C++ app.

https://www.experts-exchange.com/questions/20534181/Adding-HTML-Control-to-Visual-C-program.html

Now that I have succeeded in it, and also wrote some HTML code to it, I face a new problem.

I need to insert HTML content to the already exisitng content I have, there is away to do it using :

        // iweb was already obtained

     if (!ibody)
         iweb->get_body (&ibody);
     
     if (ibody)
     {
          ibody->insertAdjacentHTML (_bstr_t("afterBegin"), _bstr_t(str) );
          ibody->Release ();
     }


        or

     if (!iall)
         iweb->get_all (&iall);


     _variant_t vtag = 6;
     _variant_t vindex = 1;

     IDispatch  * iTag;

     if (iall)
     {
          iall->item (vtag, vindex, &iTag);
          if (iTag)
          {
               IHTMLTable  * irow;
               iTag->QueryInterface(IID_IHTMLTable,(void**)&irow);
                        //now use it
               iTag->Release ();
          }

          iall->Release ();
     }


this works well, but what if I need to insert at a specific location? the way I saw in the documantations says, I need to search for it and then use a specific COM interface per tag (e.g. <TR>, <TABLE>) and use specific methods availaible only to that tag, and that is way more code than I wanted.

I need a generic way to search for a specific location and insert raw HTML at that specific location. can anyone help me with a technique and a code snippet ?

thanks!                    
Avatar of DanRollins
DanRollins
Flag of United States of America image

When you say " at that specific location" what are you talking about... a piece of Javascript, the start of a TR entry?  exactly what?

I have not reid this approach, but it ought to work:
You can get the innerHTML of the body text,... right?  Just search it to locate the target location.  Then insert the desired new text, and then set the innerHTML with the modified text.

-- Dan
Avatar of moshem
moshem

ASKER

This might work, but it is as "expensive" as reloading the entire page, the idea was to 'insert'

I need to insert after a specific tag...
The "expense" of the operation was not mentioned in the question :)  And IE can parse and redisplay a page in the blink of an eye...

If you can do just a little bit of parsing, you can locate that tag and ascertain its ID.  Given the ID, you can locate it in the document.all collection, and modify just it. If it does not have an ID, then you could try backing up to a larger container (for instance the TABLE tag, if the secret tag you are looking for, is say a TD tag) and if *it* has an ID, then you an locate it in the all collection, then get and update the innerHTML of that larger object.  Yes, this is hugely expensive.  Possibly a full millisecond.

How about telling us exactly what you are trying to do?  What text do you want to insert, where excatly do you want to insert it, and why are you attempting to do this?

I ask this because there are often many different ways to approach a problem and you might be looking down a complicated path when a simpler one is available.

-- Dan
Avatar of moshem

ASKER

Ok,

I am using IE to display data from my software, I am formating it with HTML and insert it on top (almost on top, there is a header).

so I an put any ID tag I want if it will help me to locate it later.

then I need to insert new HTML text so it will "push" the rest of the data down but the header remains. this is why I need to insert at specific location.

as for document.all collection, can you give a full example ? I need it to be a generic thing and not use interface specific to tag type.

thanks
ASKER CERTIFIED SOLUTION
Avatar of DanRollins
DanRollins
Flag of United States of America image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
I hit the same problem earlier. My conclusion is simple.
put_innerHTML doesn't work. Well, atleast not as documented. You can only
use it to write certain tags, mostly tags that have to do with rendering.
E.g. you can query the IHTMLElement interface from a <head> element, but you
can't use its put_innerHTML method for any tag that's valid within head.

You can fool the system however by writing for example:
x.put_innnerHTML(place,"<br><base...>);  Where x is the IHTMLElement interface
of <HEAD>.

Put MSHTML or the webbrowser in design mode and send the savesas command to
verify that this DOES work. Ofcourse you don't want the <br> there.

The -solution- is to query the IHTMLDOM... interfaces introduced with IE5. Use
the old createElement, query its IHTMLDOMNode interface and insert this by
calling AppendNode from another IHTMLDOMNode interface queried from the node
where you want to insert.

Some example code. I used this to insert a new base element into a document (when one doesn't exist).
The code works equally well with any other type of element.

IHTMLElement* pHead;
CString eTag,BaseUrl;
HRESULT hr;

// [... walk document and retrieve head element]
// [... set BaseUrl to some value]

IHTMLDOMNode* pDOMNode = NULL;

if (SUCCEEDED(hr = pHead->QueryInterface( IID_IHTMLDOMNode, (LPVOID*)&pDOMNode ))) {              

     ASSERT(pDOMNode);

     IHTMLElement* NewBase = NULL;          // A new HTML <base> element
     IHTMLDOMNode* RefDomNode = NULL;     // reference to new element inserted

     // create the new base element with href set to the URL of the document
     eTag.Format("<base href=\"%s\">",(LPCTSTR)BaseUrl);
     hr=pHTMLDocument2->createElement(eTag.AllocSysString(),&NewBase);

     // We need the IHTMLDOMNode interface of the  new <base> element to insert it
     // in the document under <head>
     IHTMLDOMNode* NewDomNode= NULL;
     if (SUCCEEDED(hr = NewBase->QueryInterface( IID_IHTMLDOMNode, (LPVOID*)&NewDomNode))) {
          hr = pDOMNode->appendChild(NewDomNode,&RefDomNode);
          if ( RefDomNode != NULL)
               RefDomNode->Release();
          NewDomNode->Release();                                                  
     }    

     // release the IHTMLElement interface to the new <base> element
     if ( NewBase != NULL)
          NewBase->Release();
     // release the IHTMLDOMNode interface of the <head> element
     pDOMNode->Release();

} // QI IHTMLDOMNode
>put_innerHTML doesn't work. Well, atleast not as documented... <head> element...

There is no need to make this harder than necessary.  I guarantee that you can use
 
    pElem->PutinnerHTML( s );

to replace <DIV>, or <TD>, or <P> with any sort of HTML, even complicated HTML such as a nested table, etc.

-- Dan
Avatar of moshem

ASKER

guys, I'll try it soon and let you know, thanks
>I guarantee that you can use pElem->PutinnerHTML( s );
>to replace <DIV>, or <TD>, or <P> with any sort of HTML,

Of course, but PutinnerHTML does not scale up to any tag outside <body>. IHTMLDOMNode works totally universal on every tag in the entire document.

Maybe today it's only the <body> tag you're working in, but tomorrow that might change. When using the DOM interfaces, you are simply prepared.

Also, I don't think it's that more difficult. If you throw away some checks and releases in my example code, you'll see that the basic code is just <create node> <add node>.

Avatar of moshem

ASKER

DanRollins

your code works (after porting to Win32) but it will only work once!

for example :

<DIV id=here></DIV>

I can insert into this tag all the text I want, but what happens next time I need to insert something in the same place but keep what I have inserted before.

I don't want to contcat the new and old insertion, the idea was to make it as efficent as possible.

thanks
Avatar of moshem

ASKER

Ok , I got it I used insertAdjacentHTML instead

thanks