再谈动态创建网页元素收藏

news/2025/2/26 21:16:20
 

再谈动态创建网页元素收藏

 | 旧一篇: 代码重温:TZoCInetChecker——一个检测网络连接的类

<script>function StorePage(){d=document;t=d.selection?(d.selection.type!='None'?d.selection.createRange().text:''):(d.getSelection?d.getSelection():'');void(keyit=window.open('http://www.365key.com/storeit.aspx?t='+escape(d.title)+'&u='+escape(d.location.href)+'&c='+escape(t),'keyit','scrollbars=no,width=475,height=575,left=75,top=20,status=no,resizable=yes'));keyit.focus();}</script>  

关键字:IHTMLOptionElementFactory,IHTMLImageElementFactory,createElement

 

1、概述

在《FAQ:操纵下拉列表》中我曾写到如何调用IHTMLDocument2::createElementIHTMLSelectElement::add动态为IHTMLSelectElement添加表项:

 

  先调用IHTMLDocument2::createElement创建一个option对象,

  HRESULT createElement(          
      BSTR eTag,               
//标签名,可以是img, area, option(ie4), frame, iframe(ie5)

      IHTMLElement **newElem   //返回对象指针
  );

  再调用IHTMLSelectElement::add将创建的option对象添加到列表中

  HRESULT add(          
      IHTMLElement 
*
element,
      VARIANT before   
//添加到哪个位置,VT_I4类型的VARIANT

  );

 

方法来自

 

<SCRIPT LANGUAGE="JScript">
var oOption = document.createElement("OPTION");
oOption.text
="Ferrari"
;
oOption.value
="4"
;
oSelect.add(oOption);
</SCRIPT>

 

但事实上类似的代码在C++中却不能工作。

 

 

2、发现

写文章时测试过脚本调用之后就放在一边了,而真正写程序用到时才发现上面的问题。MFC中有个很好用的类CDHtmlDialog,提供了类似于CDialog的行为用以实现以HTML为表现形式的对话框,像Visual Studio 7/8的的Wizard,添加类等对话框就使用了类似的技术。CDHtmlDialog的核心在于MFC中被用得出神入化的宏,再加上一系列DDX函数来实现HTML content和应用程序的数据的数据交换。比如下面的函数可以实现读取某个能够匹配szIdIHTMLSelectElement中被选中的值,或选中value所指定的那个IHTMLOptionElement

 

void CDHtmlDialog::DDX_DHtml_SelectString(LPCTSTR szId, CString&  value, BOOL bSave)
{
    CComPtr
<IHTMLDocument2>
 sphtmlDoc;
    GetDHtmlDocument(
&
sphtmlDoc);
    
if (sphtmlDoc ==
 NULL)
        
return
;

    COleVariant varEmpty, varIndex;

    CComPtr
<IHTMLSelectElement>
 spSelect;
    CComPtr
<IDispatch>
 spdispOption;
    CComPtr
<IHTMLOptionElement>
 spOption;
    CComBSTR bstrText;
    HRESULT hr 
=
 S_OK;
    
long lIndex=-1
;

    hr 
= GetElementInterface(szId, __uuidof(IHTMLSelectElement), (void **&
spSelect);
    
if (spSelect ==
 NULL)
        
return
;

    
if
 (bSave)
    
{
        
// get the selected item

        value.Empty();
        spSelect
->get_selectedIndex(&
lIndex);
        
if (lIndex >= 0
)
        
{
            varIndex 
=
 lIndex;

            spSelect
->item(varIndex, varEmpty, &
spdispOption);
            
if
 (spdispOption)
            
{
                spdispOption
->QueryInterface(__uuidof(IHTMLOptionElement), (void **&
spOption);
                
if
 (spOption)
                
{
                    spOption
->get_text(&
bstrText);
                    
if
 (bstrText)
                        value 
=
 bstrText;
                }

            }

        }

    }

    
else
    
{
        bstrText.Attach(value.AllocSysString());
        lIndex 
=
 Select_FindString(spSelect, bstrText, FALSE);
        spSelect
->
put_selectedIndex(lIndex);
    }

}


 

MFC只提供了比较基本的交换函数,更复杂的的数据交换则需要自己实现了。比如我有一个CStringArray,希望能以CStringArray中的每个字符串为Value动态创建IHTMLOptionElement并添加到IHTMLSelectElement,或者将所有IHTMLSelectElementIHTMLOptionElementValue作为字符串保存到CStringArray中,该怎么办呢?

 

很简单啊,用文章开头说到的方法动态创建IHTMLOptionElement不就行了。但实际上虽然程序运行不保错,但MSHTML似乎没有任何反应。看来createElement只能用脚本调用了,《FAQ: 如何动态创建并访问网页元素》一文中讨论的问题也证明了这一点。同时,在那篇文章中能解决问题的insertAdjacentHTML对于IHTMLSelectElementIHTMLOptionElement也没有用。

 

 

3、方案

第一反应当然是上网搜了。我于是发现有个朋友在FAQ:操纵下拉列表》的CSDN文档中心版本作了如下的回复:

 

According to MSDN, “Before you can add an element to a collection, you must create it first by using  the IHTMLDocument2::createElement method”. Actually, it does not work.

See the detail 
in
 codeproject  

 

codeproject 的这篇文章,指的是《How to operate controls in an HTML file, using C++》,其中说到应该使用IHTMLOptionElementFactory来创建IHTMLOptionElement。而IHTMLOptionElementFactory需要从IHTMLWindow2(该文说应该从DocumentScript来获得IHTMLWindow2,其实从DocumentparentWindow更为直接)的Option来得到,再看MSDN,我们发现同样还有一个IHTMLImageElementFactory接口(从IHTMLWindow2Image得到)用于创建IHTMLImgElement。看来要么这两个接口有特殊的地方,要么就是历史遗留问题了。因为令人感到奇怪的是MSDN中关于IHTMLDocument2::createElement方法的说明对这两个接口只字不提:

 

In Microsoft Internet Explorer 4.0, the only new elements you can create are img, area, and option. As of Internet Explorer 5, you can create all elements programmatically, except for frame and iframe. In addition, the properties of these created elements are read/write and can be accessed programmatically. Before you use new objects, you must explicitly add them to their respective collections or to the document. To insert new elements into the current document, use the IHTMLDOMNode::insertBefore or IHTMLDOMNode::appendChild methods. 

Attributes can be included with the eTag as long as the entire string is valid HTML. 

 

 

4、示例

下面的函数演示了如何使用IHTMLOptionElementFactory来实现前面我们希望完成的功能,

 

void CMyDHtmlDialog::DDX_DHtml_SelectOptions(CDataExchange* pDX, LPCTSTR szId, CMIEStringArray&  value)
{
    CComPtr
<IHTMLDocument2>
 sphtmlDoc;
    GetDHtmlDocument(
&
sphtmlDoc);
    
if (sphtmlDoc ==
 NULL)
        
return
;

    COleVariant varEmpty, varIndex;

    HRESULT hr 
=
 S_OK;
    CComPtr
<IHTMLSelectElement>
 spSelect;
    hr 
= GetElementInterface(szId, __uuidof(IHTMLSelectElement), (void **&
spSelect);
    
if (spSelect ==
 NULL)
        
return
;

    
if (pDX->
m_bSaveAndValidate)
    
{
        
// get the selected item

        value.RemoveAll();
        
long length = 0
;
        spSelect
->get_length(&
length);

        
for (long lIndex = 0; lIndex < length; lIndex++
)
        
{
            varIndex 
=
 lIndex;
            CComPtr
<IDispatch>
 spdispOption;
            spSelect
->item(varIndex, varEmpty, &
spdispOption);
            
if
 (spdispOption)
            
{
                CComPtr
<IHTMLOptionElement>
 spOption;
                spdispOption
->QueryInterface(__uuidof(IHTMLOptionElement), (void **&
spOption);
                
if
 (spOption)
                
{
                    CComBSTR bstrText;
                    spOption
->get_text(&
bstrText);
                    
if
 (bstrText)
                    
{
                        value.Add(bstrText);
                    }

                }

            }

        }

    }

    
else
    
{
        
long length = 0
;
        spSelect
->get_length(&
length);
        
for (long lIndex = 0; lIndex < length; lIndex++
)
        
{
            spSelect
->remove(0
);
        }


        CComQIPtr
<IHTMLWindow2> spWindow;
        
if ( FAILED(sphtmlDoc->get_parentWindow(&spWindow)) || !
spWindow )
            
return
;

        CComQIPtr
<IHTMLOptionElementFactory>
 spOptionFactory;
        
if ( FAILED(spWindow->get_Option(&spOptionFactory)) || !
spOptionFactory )
            
return
;

        
// Add each items to selection

        for (long lIndex = 0; lIndex < value.GetCount(); lIndex++)
        
{
            CString strOption 
=
 value[lIndex];

            
// Get current item in the dictionary

            IHTMLOptionElement * pOption;
            VARIANT_BOOL vt_b 
= lIndex == 0 ?
 VARIANT_TRUE : VARIANT_FALSE;
            
if ( FAILED(spOptionFactory->
create(CComVariant(strOption), CComVariant(strOption), 
                CComVariant(vt_b), CComVariant(vt_b), 
&pOption)) || !
pOption )
                
continue
;

            
// Add to selection tag

            if ( FAILED(spSelect->add((IHTMLElement*)pOption, CComVariant(lIndex))) )
                
continue
;
        }

    }

}

 

 

参考文献:

FAQ:操纵下拉列表

FAQ: 如何动态创建并访问网页元素

CSDN技术中心 FAQ:操纵下拉列表

How to operate controls in an HTML file using C++

http://www.niftyadmin.cn/n/4819751.html

相关文章

VC++6.0中使用OpenGL

来源:计算机世界:http://www2.ccw.com.cn/99/9934/9934b16.asp VC &#xff0b; &#xff0b; 6.0 中使 用OpenGL 清 华 大 学 李 勇 ----要 学 习OpenGL 编 程&#xff0c; 希 望 读 者 具 备 基 本 的 图 形 知 识。 本 文 使 用 基 于Visual C &#xff0b; &#xff0b; …

实验指令(3)

三层链路聚合&#xff1a; [SWB]interface Route-Aggregation 1 [SWB-Bridge-Aggregation1]exit [SWB]interface range G 1/0/21 to G 1/0/23 [SWB-if-range]port link-mode route [SWB-if-range]port link-aggregation group 1 [SWB-if-range]exit [SWB]interface route-Aggre…

vue tslint报错: Calls to 'console.log' are not allowed

使用Vue CLI 3 的 vue create 创建vuets 项目&#xff0c;使用默认配置&#xff0c; 控制台报警告Calls to console.log are not allowed&#xff0c;解决&#xff1a; 在tslint.json中的rules下 添加&#xff1a; "no-console": false。无需向在tsling.config中添加…

强制和别人QQ聊天代码

http://wpa.qq.com/msgrd?V1&Uin10001&Siteioshenmue&Menuyes http://wpa.qq.com/msgrd?V1&Uin*****&Siteioshenmue&Menuyes 收集自网络,请把*****处替换为你想要聊天的QQ号码,就像最上面的那个要和腾讯老总说话的10001一样.

如何编程动态改变IE的代理服务器设置, 并且使之马上生效!

如何编程动态改变IE的代理服务器设置, 并且使之马上生效! 选择自 sxxny 的 Blog 关键字 IE 代理服务器出处 最近有很多朋友都在讨论如何改变IE的代理服务器设置, 刚好我最近做的一个东西里面用到了这样的功能. 拿出来和大家共享一下. 用到的关键函数是wininet库里面的…

正则表达式学与练

这两天,开始学习正则表达式,并且开始练习了. 首先,我没有下载正则表达式测试器的程序,因为我使用的是一个网页测试器,它的代码来自于: 正则表达式练习器 http://www.codefans.com/ArticleView/Article_5600.html 打开上面的页面&#xff0c;把Html代码复制下来&#xff0c;保…

PHP on Windows Azure 入门教学系列(3) ——在Windows Azure中部署Wordpress

公告&#xff1a;本博客为微软云计算中文博客的镜像博客。部分文章因为博客兼容性问题&#xff0c;会影响阅读体验。如遇此情况&#xff0c;请访问原博客。 本文是PHP on Windows Azure 入门教学系列第三篇文章。请上一篇文章中&#xff0c;我们已经成功的在Windows Azure中运行…

C语言左值和右值,以及操作符属性总结

《C和指针》这本书上讲到左值和右值&#xff0c;概念有点含糊 据我自己的理解&#xff1a; 左值&#xff1a;标识的应该是个存储位置&#xff0c;内存中的位置&#xff0c;左值可以是个变量名&#xff0c;或者是个表达式&#xff0c;但表达式必须表示的是个内存位置 右值&#…