Как получить элементы по имени в Delphi Chromium Embedded?

Чтобы получить конкретный узел DOM, встроенный в текущий веб-документ, из экземпляра TChromium, используя его идентификатор, вы используете ICefDomDocument.getElementById(). Но как найти элементы по атрибуту NAME? Javascript имеет метод document.getElementsByName(), а TWebBrowser (обертывающий IE) имеет аналогичный вызов, но я не могу понять, как это сделать с TChromium. Мне нужно найти некоторые элементы DOM, которые имеют атрибуты NAME, но не имеют атрибутов ID. Я искал модуль ceflib и не нашел ничего, что могло бы это сделать.

Побочный вопрос. Если у кого-то есть ссылка на сайт или документ в стиле «рецептов» TChromium, я могу ее использовать.

ОБНОВЛЕНИЕ: в ожидании ответа я придумал следующий код для выполнения getElementsbyName(). Мне нужно что-то более быстрое, чем сканирование всего дерева DOM. Если вы видите что-то не так в коде, дайте мне знать:

type
    TDynamicCefDomNodeArray = array of ICefDomNode;


// Given a Chromium document interface reference and a NAME attribute to search for,
//  return an array of all DOM nodes whose NAME attribute matches the desired.
function getElementsByName(ADocument: ICefDomDocument; theName: string): TDynamicCefDomNodeArray;

    // Get all the elements with a particular NAME attribute value and return
    //  an array of them.
    procedure getElementsByName1(intfParentNode: ICefDomNode; theName: string; var aryResults: TDynamicCefDomNodeArray);
    var
        oldLen: integer;
        intfChildNode: ICefDomNode;
        theNameAttr: string;
    begin
        Result := nil;
        intfChildNode := nil;

        if Assigned(intfParentNode) then
        begin
            // Attributes are case insensitive.
            theNameAttr := intfParentNode.GetElementAttribute('name');

            if AnsiSameText(theNameAttr, theName) then
            begin
                // Name attribute match.  Add it to the results array.
                oldLen := Length(aryResults);
                SetLength(aryResults, oldLen + 1);
                aryResults[oldLen] := intfParentNode;
            end; // if AnsiSameText(intfParentNode.Name, theName) then

            // Does the parent node have children?
            if intfParentNode.HasChildren then
            begin
                intfChildNode := intfParentNode.FirstChild;

                // Scan them.
                while Assigned(intfChildNode) do
                begin
                    getElementsByName1(intfChildNode, theName, aryResults);

                    if Assigned(intfChildNode) then
                        intfChildNode := intfChildNode.NextSibling;
                end;
            end; // if intfParentNode.HasChildren then
        end; // if Assigned(intfParentNode) then
    end;

    // ---------------------------------------------------------------

var
    intfCefDomNode: ICefDomNode;
begin
    intfCefDomNode := nil;
    Result := nil;

    if Assigned(ADocument) then
    begin
        // Check the header.
        intfCefDomNode := ADocument.Document;

        if Assigned(intfCefDomNode) then
        begin
            // Check the parent.
            getElementsByName1(intfCefDomNode, theName, Result);
        end; // if Assigned(intfCefDomNode) then
    end; // if Assigned(ADocoument) then
end;

// ---------------------------------------------------------------

person Robert Oschler    schedule 20.05.2012    source источник
comment
Я не думаю, что разумно смешивать и сочетать технологии 10-летней давности с современными технологиями и ожидать, что это станет найденным и стабильным решением. В данном конкретном случае TChromium не поддерживает Delph 6. code.google.com/p/delphichromiumembedded   -  person Jeroen Wiert Pluimers    schedule 20.05.2012
comment
@Jeroen, однако TChromium не поддерживает Delphi 6 (для него нет пакета ), но это не значит, что он не может там работать. У меня есть Delphi 2009, который также не поддерживается, но, глядя на исходный код, нет ничего, что могло бы помешать использованию там ;-)   -  person TLama    schedule 20.05.2012
comment
@TLama, если мне не изменяет память, Delphi 7 представила довольно много исправлений, касающихся упаковки COM-материалов. Это может быть причиной отказа от поддержки Delphi 6. Я бы рекомендовал Роберту проверить это предположение с командой TChromium.   -  person Jeroen Wiert Pluimers    schedule 20.05.2012
comment
@TLama - я так думаю. Я использую три модуля Delphi, связанные с Chromium: cef, ceflib, ceffilescheme. Кроме того, у меня есть TChromium в моей палитре компонентов, и я могу добавить его в форму.   -  person Robert Oschler    schedule 20.05.2012


Ответы (1)


Нет такой функции, как JavaScript getElementsByName или MSHTML getElementsByName, встроенный в файл Chromium Embedded и его оболочки Delphi. Вы можете решить эту проблему, только перебирая все элементы DOM, например. создав свой собственный класс посетителя DOM следующим образом:

Обратите внимание, что процедура VisitDom является асинхронной, поэтому она возвращается немедленно (фактически до того, как посетитель DOM закончит свою visit) и работает со снимком DOM в момент выполнения.

type
  TElementNameVisitor = class(TCefDomVisitorOwn)
  private
    FName: string;
  protected
    procedure visit(const document: ICefDomDocument); override;
  public
    constructor Create(const AName: string); reintroduce;
  end;

procedure ProcessElementsByName(const AFrame: ICefFrame; const AName: string);
var
  Visitor: TElementNameVisitor;
begin
  if Assigned(AFrame) then
  begin
    Visitor := TElementNameVisitor.Create(AName);
    AFrame.VisitDom(Visitor);
  end;
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
  ProcessElementsByName(Chromium1.Browser.MainFrame, 'NameAttributeValue');
end;

{ TDOMElementNameVisitor }

constructor TElementNameVisitor.Create(const AName: string);
begin
  inherited Create;
  FName := AName;
end;

procedure TElementNameVisitor.visit(const document: ICefDomDocument);

  procedure ProcessNode(ANode: ICefDomNode);
  var
    Node: ICefDomNode;
  begin
    if Assigned(ANode) then
    begin
      Node := ANode.FirstChild;
      while Assigned(Node) do
      begin
        if Node.GetElementAttribute('name') = FName then
        begin
          // do what you need with the Node here
          ShowMessage(Node.GetElementAttribute('value'));
        end;
        ProcessNode(Node);
        Node := Node.NextSibling;
      end;
    end;
  end;

begin
  ProcessNode(document.Body);
end;
person TLama    schedule 20.05.2012
comment
Re: изобретать велосипед. Не специально, просто используя то, что я нашел при исследовании использования Chromium с Delphi (см. мой ответ на ваш комментарий к основному сообщению). - person Robert Oschler; 20.05.2012
comment
Я так понимаю, вызов визита с потомком TCefDomVisitorOwn реализует шаблон посетителя? Другими словами, CEF будет применять этот шаблон ко всем узлам в DOM, обрабатывая для вас рекурсивный спуск по дереву узлов? Это очень круто, если так, но я хочу быть абсолютно уверенным, поэтому прошу подтверждения. - person Robert Oschler; 20.05.2012
comment
Нет, итерацию вы должны сделать самостоятельно. Отличие метода VisitDom в том, что он делает копию (моментальный снимок) текущего состояния DOM. В моем примере TElementNameVisitor.visit работает с копией документа, а не с самим документом (который можно изменить во время повторения). Не знаю, быстрее ли, просто безопаснее. - person TLama; 20.05.2012
comment
Забыл заметить, что VisitDom является асинхронным, поэтому я использовал имя ProcessElementsByName, а не getElementsByName, потому что мне пришлось бы ждать, пока посетитель закончит visit. - person TLama; 20.05.2012
comment
Так или иначе, некоторое время назад был выпущен Delphi Chromium Embedded 3. - person TLama; 24.09.2012
comment
@TLama Можно ли щелкнуть этот узел, если это кнопка? - person user3060326; 10.05.2014