Поиск по блогу

среда, 18 февраля 2009 г.

Как вызывать функции JavaScript на странице, загруженной в TWebBrowser в Delphi

Немного теории для тех, кто осваивает возможности Delphi при работе с интернетом.

Как вызвать функцию JavaScript на странице в TWebBrowser-е.

Зачем нам это может пригодиться при парсинге? Примеров может быть много, причем совершенно разноплановых. Приведу один.

парсинг страницы с AJAX
Сейчас на некоторых сайтах активно используется AJAX. Рассмотрим ситуацию, когда на веб-странице расположены несколько объектов SELECT. При первой загрузке страницы активен только первый. После выбора определенного OPTION-а без перезагрузки страницы формируется набор значений для второго SELECT-а и он становится активным. И так далее. Если просто присвоить свойству selected (iOption.selected := true;) одной из записей набора OPTIONS значение true, то JavaSctript, повешенный на onchange этого селекта, не сработает. Скрипт надо явно запустить.

Итак, решение. У объекта типа IHTMLWindow2, который представляет собой экземпляр HTML-документа, для наших целей есть метод execScript. Ему надо передать 2 строковых параметра:

1 — непосредственно строка вызова функции;
2 — язык, который используется (у нас — JavaScript).

Получить объект окна просто. Подключаем библиотеку MSHTML, объявляем переменные типов IHTMLDocument2, IHTMLWindow2.

uses
..., MSHTML;

Doc : IHTMLDocument2;
HTMLWindow : IHTMLWindow2;


Для получения объекта IHTMLWindow2 используем свойство parentWindow у объекта Document браузера (после того, как документ полностью загрузится).

WebBrowser.Document.QueryInterface(IHTMLDocument2, Doc);
HTMLWindow := Doc.parentWindow;


Перед тем, как дальше с полученным объектом, надо проверить, ассигновался ли он вообще:

if not Assigned(HTMLWindow) then Exit;


В качестве примера привожу листинг отдельной процедуры. Она в заданном SELECT-е (SelectEl) выбирает OPTION с определенным текстом (TextStr). И запускает JavaScript, повешенный на onchange селекта. Саму по себе, без контекста, вы ее не запустите, но синтаксис будет понятен.

procedure TMainF.SelectItemByTextFromSelectEl(TextStr: string;
SelectEl: IHTMLSelectElement; SelectName: string);
var
i, t : integer;
iDisp : IDispatch;
iColl : IHTMLElementCollection;
iOption : IHTMLOptionElement;
JSFStr : string;
begin
iDisp := SelectEl.tags('OPTION');
iDisp.QueryInterface(IHTMLElementCollection, iColl);
if not Assigned(iColl) then
begin
ShowMessage('Not assigned!'); exit;
end;

i := 0;
while i <= iColl.length-1 do
begin
iDisp := iColl.item(i,0);
iDisp.QueryInterface(IHTMLOptionElement, iOption);
if Assigned(iOption) then
begin
if iOption.text = TextStr then
begin
iOption.selected := true;
if not Assigned(HTMLWindow) then Exit;
try
JSFStr := 'setTimeout(''__doPostBack(\''' + SelectName + '\'',\''\'')'', 0)';
HTMLWindow.execScript(JSFStr, 'JavaScript'); // вызов функции
except
end;

for t := 0 to WaitPause-1 do
begin
Sleep(1000); Application.ProcessMessages;
end;
break;
end;
end;
inc(i);
end;

end;

Не забывайте про экранирование эскейп-символов при помощи обратных слешей. Если в функцию надо передавать какие-либо параметры, то при подготовке строки с вызовом функции удобно пользоваться Format-ом.

JSFStr := Format('Foo("%s",%d)', [S, I]);


Следует отметить, что метод execScript предназначен для запуска функций, которые не возвращают значения, или таких функций, у которых нам возвращаемое значение не нужно.


Чтобы быть в курсе обновлений блога, можно подписаться на RSS.

Статьи схожей тематики:



5 комментариев:

  1. спасибо, полезно

    ОтветитьУдалить
  2. А как в случае наоборот - установить callback ф-цию, т.е. при срабатывани javacript ф-ци в загруженном документе вызвать собственную и обработать.

    ОтветитьУдалить
  3. Вот мне тоже очень интересно, как можно вызвать собственную функцию или хотя бы вернуть результат. Не подскажете?

    ОтветитьУдалить
  4. Спасибо за ценную информацию. Интересно, а можно как-то красиво отлавливать момент, когда ява отработала (у меня она грузит новую странцу, поэтому занимает много времени), кроме как ставить паузу? Ипользование Browser.ReadyState не помогает :(

    ОтветитьУдалить
  5. И мне интересно как дождаться выполнения скрипта?

    ОтветитьУдалить

Комментарии модерируются, вопросы не по теме удаляются, троллинг тоже.

К сожалению, у меня нет столько свободного времени, чтобы отвечать на все частные вопросы, так что, может, свой вопрос лучше задать на каком-нибудь форуме?

Поделиться