是否有任何事件可以检测到网页上活动元素的更改时间?例如,当用户聚焦编辑框时。当TWebBrowser文档中的活动元素发生变化时检测到
我知道我可以检查计时器中的活动元素,但如果可能的话,我宁愿避免这种情况。
是否有任何事件可以检测到网页上活动元素的更改时间?例如,当用户聚焦编辑框时。当TWebBrowser文档中的活动元素发生变化时检测到
我知道我可以检查计时器中的活动元素,但如果可能的话,我宁愿避免这种情况。
这不是 - 完全 - 你的问题的完整答案,但希望能为你带来最大的方式。
(对于谁通过一个类似的q来到这里未来的读者:
假设你有一个类型库进口单位像SHDOCVW,MSHTML自动化/ COM服务器或一个MS Word的有时, Delphi的类型库导入器将事件支持添加到它生成的Delphi TObject后代包装中,例如TWebBrowser,OnNavigateComplete等事件。其他时候,它不能或不会生成Delphi包装类,但仍可以附加到服务器通过多种方法之一对事件进行处理,比如通过创建一个如下所示的事件对象,它在Delphi代码中连接服务器对象的事件和事件处理程序。
处理接口事件基本上涉及到定义一个Delphi类,它实现了一个IDispatch接口,然后将该接口连接到您希望通知其事件的Ole或COM对象。然后,当Ole/COM中的事件发生在接口“后面”时,它会像调用它的方式一样调用您的IDispatch。你如何处理事件通知完全取决于你;下面的代码将它们传递给TForm1的一个方法。 )
下的EventObject紧密基于一个由TeamB的德博拉·佩特(她有她的关于自动化网站使用Delphi的一个很好的部分在Borland NGS发表在2003年11月 - http://www.djpate.freeserve.co.uk/Automation.htm)。该对象非常通用,因为它不限于处理任何特定Ole/COM对象的事件。
// The following code is intended to illustrate methods of detecting that the
// active element in an Html page has changed. See the comments in the AnEvent
// procedure about how exactly to detect such a change.
//
// The code also illustrates how to handle a single event, e.g. onbeforeeditfocus
// of an Events objects such as HtmlDocumentEvents or HtmlDocumentEvents2 (see MSHTML.Pas)
// or all the events the events interface contains.
type
TInvokeEvent = procedure(Sender : TObject; DispIP : Integer) of Object;
TEventObject = class(TInterfacedObject, IDispatch)
private
FOnEvent: TInvokeEvent;
FSinkAllEvents : Boolean;
protected
function GetTypeInfoCount(out Count: Integer): HResult; stdcall;
function GetTypeInfo(Index, LocaleID: Integer; out TypeInfo): HResult; stdcall;
function GetIDsOfNames(const IID: TGUID; Names: Pointer;
NameCount, LocaleID: Integer; DispIDs: Pointer): HResult; stdcall;
function Invoke(DispID: Integer; const IID: TGUID; LocaleID: Integer;
Flags: Word; var Params; VarResult, ExcepInfo, ArgErr: Pointer): HResult; stdcall;
public
constructor Create(const AnEvent : TInvokeEvent; SinkAll : Boolean);
property OnEvent: TInvokeEvent read FOnEvent write FOnEvent;
property SinkAllEvents: Boolean read FSinkAllEvents;
end;
type
TForm1 = class(TForm)
[ ... ]
private
{ Private declarations }
procedure AnEvent(Sender : TObject; DispID : Integer);
procedure AnotherEvent(Sender : TObject; DispID : Integer);
public
{ Public declarations }
Doc : IHtmlDocument3;
DocEvent,
DocEvent2: OleVariant;
Cookie : Longint;
CPC : IConnectionPointContainer;
Sink : IConnectionPoint;
PrvActiveElement : IHTMLElement;
Events : Integer;
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
constructor TEventObject.Create(const AnEvent: TInvokeEvent; SinkAll : Boolean);
begin
inherited Create;
FOnEvent := AnEvent;
FSinkAllEvents := SinkAll;
end;
function TEventObject.GetIDsOfNames(const IID: TGUID; Names: Pointer;
NameCount, LocaleID: Integer; DispIDs: Pointer): HResult;
begin
Result := E_NOTIMPL;
end;
function TEventObject.GetTypeInfo(Index, LocaleID: Integer;
out TypeInfo): HResult;
begin
Result := E_NOTIMPL;
end;
function TEventObject.GetTypeInfoCount(out Count: Integer): HResult;
begin
Result := E_NOTIMPL;
end;
function TEventObject.Invoke(DispID: Integer; const IID: TGUID;
LocaleID: Integer; Flags: Word; var Params; VarResult, ExcepInfo,
ArgErr: Pointer): HResult;
begin
if SinkAllEvents then begin
if Assigned(FOnEvent) then
FOnEvent(Self, DispID);
Result := S_OK;
end
else begin
if (Dispid = DISPID_VALUE) then begin
if Assigned(FOnEvent) then
FOnEvent(Self, DispID);
Result := S_OK;
end
else Result := E_NOTIMPL;
end;
end;
procedure TForm1.AnEvent(Sender : TObject; DispID : Integer);
var
Doc2 : IHTMLDocument2;
E : IHTMLElement;
begin
Inc(Events);
Doc.QueryInterface(IHTMLDocument2, Doc2);
E := Doc2.activeElement;
// NB: When an <INPUT> text edit is receiving focus, the following code is triggered twice
// or more with different values of Pointer(Doc2.activeElement). So, "(E <> PrvActiveElement)"
// doesn't seem a very effective test that the active element has changed. However,
// testing E's Name, ID, etc should provide a useful test.
if (E <> Nil) and (E <> PrvActiveElement) and E.isTextEdit then begin
if PrvActiveElement <> Nil then
PrvActiveElement := E;
Caption := Format('Something happened: Element Tagname: %s, Name: %s, %d, %d, %p',
[E.TagName, E.GetAttribute('Name', 0), DispID, Events, Pointer(Doc2.activeElement)]);
end;
end;
procedure TForm1.AnotherEvent(Sender : TObject; DispID : Integer);
begin
Caption := Format('Something else happened: %d', [DispID]);
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
Memo1.Lines.LoadFromFile('D:\aaad7\html\postdata.htm');
end;
procedure TForm1.btnLoadClick(Sender: TObject);
var
V : OleVariant;
Doc2 : IHtmlDocument2;
begin
WebBrowser1.Navigate('about:blank');
Doc := WebBrowser1.Document as IHTMLDocument3;
Doc.QueryInterface(IHTMLDocument2, Doc2);
V := VarArrayCreate([0, 0], varVariant);
V[0] := Memo1.Lines.Text;
try
Doc2.Write(PSafeArray(TVarData(v).VArray));
finally
Doc2.Close;
end;
DocEvent := TEventObject.Create(Self.AnEvent, cbSinkAll.Checked) as IDispatch;
if cbsinkAll.Checked then begin
CPC := Doc2 as IConnectionPointContainer;
Assert(CPC <> Nil);
OleCheck(CPC.FindConnectionPoint(HTMLDocumentEvents, Sink));
OleCheck((Sink as IConnectionPoint).Advise(DocEvent, Cookie));
end
else
Doc.onbeforeeditfocus := DocEvent;
end;
请注意TForm1.AnEvent中的注释。如果您检查cbSinkAll复选框 并在包含多个INPUT框的页面上运行代码,您会注意到AnEvent在输入相同的 INPUT框时触发了几次,每次都有不同的Doc2.ActiveElement值。我不确定这是为什么,但这确实意味着比较Doc2.ActiveElement属性的当前值 与以前的值无法有效检测到重点在Html页面上的更改 。然而,比较元素的属性,例如它的名字或ID似乎提供了可靠的检查。
两个注意事项:
[从MSHTML.Pas摘录]
HTMLDocumentEvents = dispinterface
['{3050F260-98B5-11CF-BB82-00AA00BDCE0B}']
function onhelp: WordBool; dispid -2147418102;
[...]
procedure onbeforeeditfocus; dispid 1027;
end;
如果您只想为单个元素(而不是整个文档)处理事件,则可以将事件接收器(此处为“TEventObject”对象)分配给给定元素事件(由'HTMLElementEvents2'接口提供)。所有在['本章'](http://msdn.microsoft.com/en-us/library/bb508508%28v=vs.85%29.aspx#Sink)中描述的内容。附:我不会亲自将sink对象存储在OleVariant变量中。我会直接使用'IDispatch'。 [+ 1ed] – TLama 2014-08-27 16:54:29
@TLama:确实(关于单个事件)。感谢您的圣人意见/建议。正如你可能猜到的那样,我决定今天是尝试和掌握自动化事件的一天(我仍然惊讶于比尔Ly在D2/D3时代把所有这些东西都弄清楚了)。我同意绕过OleVariant,但后来很多提问者(这里不是OP)似乎使用后期绑定,并在可能时避免使用接口。 – MartynA 2014-08-27 17:02:18
我再次更新了答案,重点介绍了使用ConnectionPoint,这偶尔会使它更短。 – MartynA 2014-08-28 06:45:27
你有你的操控下html文件? – whosrdaddy 2014-08-27 12:48:16
@whosdaddy - 是的,我有权访问IHTMLDocumentx – norgepaul 2014-08-27 12:53:30
不,我是指实际的网页? – whosrdaddy 2014-08-27 13:06:49