IV. Librairie HTML▲
Jusqu'ici nous avons navigué sur une page sans regarder son contenu.
Une page internet est codée en langage HTML.
Si vous ne connaissez pas ce langage, vous pouvez consulter ce tutoriel : Les bases du HTML.
Ce langage étant structuré, nous allons lire sa structure à l'aide de la librairie Microsoft HTML Object Library.
Commençons donc par référencer cette librairie dans le menu : Outils => Références ....
IV-A. Parcourir un document HTML▲
Afin de parcourir un document HTML en provenance d'internet par exemple, il faut d'abord le charger.
Chargeons la page de recherche Google dans un navigateur.
Utilisons pour cela le code vu dans le chapitre précédent :
Public
Sub
CreerNavigateur
(
)
Dim
oNav As
SHDocVw.InternetExplorer
Set
oNav =
New
SHDocVw.InternetExplorer
oNav.Visible
=
True
oNav.navigate
"http://google.fr"
End
Sub
Cette fonction ouvre Internet Explorer et navigue vers la page de recherche de Google.
Il est également possible d'ouvrir la page dans un contrôle WebBrowser, la suite du code serait identique.
Vous pouvez visualiser la source en cliquant sur la page avec le bouton droit et en choisissant "Afficher la source".
Le code affiché est le code HTML ; il est difficilement exploitable en l'état par code VBA.
En fonction de votre version d'Internet Explorer, vous avez peut-être accès aux outils de développements (dans le menu Outils ou F12).
Cet outil s'avère très pratique pour analyser une page HTML.
Les contrôles navigateurs ont une propriété Document de type Object.
Cette propriété est une passerelle entre la librairie Microsoft Internet Controls et la librairie Microsoft HTML Object Library.
C'est en fait un objet de type HTMLDocument référencé dans la librairie HTML.
Pour profiter de l'autocomplétion de VBA, nous allons déclarer un objet de ce type afin d'y affecter la valeur de cette propriété.
Public
Sub
CreerNavigateur
(
)
Dim
oNav As
SHDocVw.InternetExplorer
Dim
oDoc As
MSHTML.HTMLDocument
Set
oNav =
New
SHDocVw.InternetExplorer
oNav.Visible
=
True
oNav.navigate
"http://google.fr"
Set
oDoc =
oNav.document
End
Sub
Si on exécute le code, on obtient une erreur :
Erreur d'exécution '-2147467259(80004005)':
Erreur Automation
Erreur non spécifiée
Cette erreur est due au fait que l'on essaye de lire la propriété Document avant que celui ne soit chargé.
Il faut donc ajouter une temporisation afin d'attendre le chargement de la page.
Pour vérifier l'état du navigateur, il y a deux propriétés :
- ReadyState : indique l'état de chargement de la page (en cours, chargée...)
- Busy : indique si le navigateur est occupé.
Il est possible que le chargement soit terminé, mais que le navigateur soit toujours occupé à faire diverses tâches.
Nous allons utiliser ces deux propriétés pour nous assurer que tout est bien chargé et que les objets HTML sont tous présents dans l'objet Document.
Une simple boucle suffirait :
While
oNav.readyState
<>
READYSTATE_COMPLETE Or
oNav.Busy
=
True
DoEvents
Wend
Ce code attend que le chargement de la page soit terminé et que le navigateur ne soit plus occupé.
DoEvents peut être nécessaire pour que certains éléments se chargent.
Sans DoEvents, il est possible qu'on reste bloqué dans une boucle sans fin.
Mais comme nous aurons probablement besoin de faire cette même temporisation plusieurs fois, nous allons créer une petite fonction.
Nous en profiterons pour ajouter un timeout qui évitera une boucle sans fin en cas de blocage du navigateur.
' Attend que la page internet soit chargée
' pTimeOut est un time out en secondes (WaitIE vaut True si Timeout)
Public
Function
WaitIE
(
oIE As
InternetExplorer, Optional
pTimeOut As
Long
=
0
) As
Boolean
Dim
lTimer As
Double
lTimer =
Timer
Do
DoEvents
If
oIE.readyState
=
READYSTATE_COMPLETE And
Not
oIE.Busy
Then
Exit
Do
If
pTimeOut >
0
And
Timer -
lTimer >
pTimeOut Then
WaitIE =
True
Exit
Do
End
If
Loop
End
Function
Cette fonction peut être placée dans n'importe quel module VBA de l'application.
On lui donne en paramètre l'objet Navigateur que l'on veut attendre, et un timeout en secondes.
Si le navigateur n'a pas rendu la main à l'issue du timeout, la fonction renvoie True.
Public
Sub
CreerNavigateur
(
)
Dim
oNav As
SHDocVw.InternetExplorer
Dim
oDoc As
MSHTML.HTMLDocument
Set
oNav =
New
SHDocVw.InternetExplorer
oNav.Visible
=
True
oNav.navigate
"http://google.fr"
' Attente avec timeout de 10 s
If
WaitIE
(
oNav, 10
) Then
' 10 s écoulées et page non chargée
MsgBox
"Time out!"
Else
' Page chargée, on continue
Set
oDoc =
oNav.document
End
If
End
Sub
Cette fonction WaitIE pourra être appelée à chaque navigation.
Si on a précisé un timeout et qu'elle renvoie True, on ne continue pas le traitement.
Sinon, on charge le document dans l'objet oDoc.
Pour visualiser cet objet, placez un point d'arrêt (F9 sur la ligne End If), ou ajoutez une instruction Stop.
Exécutez ensuite la fonction, l'exécution s'arrête.
Affichez la fenêtre Variables Locales : Affichage => Fenêtre Variables Locales.
Vous pouvez naviguer dans l'arborescence de l'objet oDoc.
Chaque élément possède une collection ChildNodes qui permet de visualiser hiérarchiquement tous les objets HTML du document.
Dans la collection All, tous les éléments de l'arborescence sont mis à plat.
Il existe d'autres collections regroupant des objets de même type, par exemple :
forms (formulaires), links (hyperliens), images (images)...
Le nombre d'éléments dans une collection est donné par la propriété Length :
- oDoc.Images.Length => 4
Le premier élément est l'élément d'indice 0 :
- oDoc.Images(0).src => http://www.google.fr/images/close_sm.gif
Pour chercher un élément par son Identifiant, on utilisera la fonction getElementById.
Pour chercher un élément par son Nom, on utilisera la fonction getElementsByName.
IV-B. Piloter une page HTML▲
Nous souhaitons maintenant remplir la zone de texte à rechercher, puis cliquer sur le bouton de recherche par le code VBA.
Après le chargement de la page, il suffit de chercher la zone de texte dans l'arborescence d'objets et de lui affecter une valeur.
Après analyse du code source de la page, on trouve la zone de recherche :
<input autocomplete
=
off maxlength
=
2048 name
=
q class
=
"lst"
title
=
"Recherche Google"
value
=
""
size
=
57 style
=
"background:#fff;border:1px solid #ccc;border-bottom-color:#999;
border-right-color:#999;color:#000;font:18px
arial,sans-serif bold;margin:0;padding:5px 8px 0 6px;vertical-align:top"
>
La zone de texte est nommée (name=q), nous pouvons donc faire une recherche par nom.
[...
]
' Page chargée, on continue
Set
oDoc =
oNav.document
' Valeur recherchée
oDoc.getElementsByName
(
"q"
)(
0
).Value
=
"Arkham46"
End
If
End
Sub
La fonction getElementsByName renvoie un tableau car il pourrait y avoir plusieurs éléments de même nom.
Sur notre page il n'y en a qu'un ; on récupère donc le premier trouvé (indice 0) et on modifie sa valeur.
Pour l'exemple je mets "Arkham46" dans cette zone de recherche.
Ensuite on simule le clic sur le bouton de recherche.
L'élément qui soumet un formulaire est de type submit ; le voici :
Notez qu'il existe un autre bouton de type submit, celui qui correspond à la recherche nommée "J'ai de la chance".
Pour cliquer sur un bouton, il faut exécuter sa méthode Click :
Pour rechercher ce bouton, on peut utiliser la fonction getElementsByName.
Problème ici : avec la version 8 d'Internet Explorer, on se retrouve avec deux boutons de même nom sur la page.
Le code suivant fonctionne avec Internet Explorer 6, mais pas avec Internet Explorer 8 :
' Clic sur bouton
oDoc.getElementsByName
(
"btnG"
)(
0
).click
Pour Internet Explorer 8, le bouton recherché est le deuxième (donc d'indice 1) :
' Clic sur bouton
oDoc.getElementsByName
(
"btnG"
)(
1
).click
Pour pouvoir facilement retrouver un bouton, on se crée une petite fonction :
' Recherche d'un bouton par son nom dans le document
Private
Function
GetButton
(
Document As
HTMLDocument, ButtonName As
String
) As
MSHTML.HTMLInputElement
If
Document.getElementsByName
(
ButtonName).length
>
1
Then
Set
GetButton =
Document.getElementsByName
(
ButtonName)(
1
)
Else
Set
GetButton =
Document.getElementsByName
(
ButtonName)(
0
)
End
If
End
Function
Cette fonction renvoie le premier bouton s'il n'y en a qu'un, ou le deuxième s'il y en a plusieurs.
On note ici les problèmes rencontrés lors de l'automatisation d'une page qui n'a pas été prévue pour.
La page peut évoluer et le code est alors à revoir...
Maintenant qu'on sait retrouver le bouton, on clique dessus :
[...
]
' Page chargée, on continue
Set
oDoc =
oNav.document
' Valeur recherchée
oDoc.getElementsByName
(
"q"
)(
0
).Value
=
"Arkham46"
' Clic sur bouton
GetButton
(
oDoc, "btnG"
).click
End
If
End
Sub
Une autre possibilité est de soumettre le formulaire directement avec sa méthode submit sans passer par un bouton.
' Soumettre le formulaire
oDoc.Forms
(
"f"
).submit
Attention cependant car le clic sur un bouton peut effectuer certaines actions complémentaires qui ne seront pas accomplies par un submit direct.
Dans le doute, simulez le clic sur le bouton voulu.
IV-C. Événements des éléments d'une page HTML▲
On a vu précédemment que nous avions à notre disposition des événements liés au document.
Il existe aussi des événements pour chacun des éléments de la page.
À titre d'exemple, capturons le clic sur le bouton de recherche de Google.
Reprenons la fonction précédemment écrite, mais en retirant le clic sur le bouton que nous ferons manuellement.
Afin de bénéficier des événements, il faut écrire le code dans un module de classe.
Créons donc un formulaire avec un bouton nommé OpenExplorer.
Sur clic sur ce bouton, on ouvre Internet Explorer sur la page de recherche Google.
Private
Sub
OpenExplorer_Click
(
)
Dim
oNav As
SHDocVw.InternetExplorer
Dim
oDoc As
MSHTML.HTMLDocument
Set
oNav =
New
SHDocVw.InternetExplorer
oNav.Visible
=
True
oNav.navigate
"http://google.fr"
' Attente avec timeout de 10s
If
WaitIE
(
oNav, 10
) Then
' 10 s écoulées et page non chargée
MsgBox
"Time out!"
Else
' Page chargée, on continue
Set
oDoc =
oNav.document
End
If
End
Sub
Nous allons également avoir besoin de la fonction GetButton créée précédemment, collez-la en fin de module par exemple.
Il faut en premier lieu connaître le type de l'élément.
Utilisons TypeName.
[...
]
' Page chargée, on continue
Set
oDoc =
oNav.document
' Bouton Google
MsgBox
TypeName
(
GetButton
(
oDoc,"btnG"
))
End
If
End
Sub
La boîte de message nous donne le type d'élément nommé btnG : nous avons affaire à un HTMLInputElement.
Ajoutons en en-tête du module une déclaration d'objet de ce type.
Private
WithEvents BoutonGoogle As
HTMLInputElement
Grâce à l'instruction WithEvents, les événements de cet objet sont disponibles dans les listes déroulantes en haut de la page de code VBA.
Choisissez l'objet BoutonGoogle à gauche et l'événement onclick à droite.
La procédure de traitement du clic sur le bouton est générée :
Private
Function
BoutonGoogle_onclick
(
) As
Boolean
End
Function
Vous pouvez alors écrire le code désiré à l'intérieur.
Par exemple l'affichage d'un texte dans la fenêtre Exécution (affichée par CTRL + G) :
Private
Function
BoutonGoogle_onclick
(
) As
Boolean
Debug.Print
"Le bouton de recherche est désactivé !"
End
Function
L'objet BoutonGoogle est prêt, mais pour l'instant il est vide.
Pour terminer, n'oublions pas d'y affecter le bouton du formulaire après chargement de la page :
[...
]
' Page chargée, on continue
Set
oDoc =
oNav.document
' Bouton Google
Set
BoutonGoogle =
GetButton
(
oDoc,"btnG"
)
End
If
End
Sub
Nous pouvons tester l'événement en affichant le formulaire.
Notez que par défaut l'événement remplace l'événement standard.
La recherche Google ne s'effectue pas.
Pour que l'événement de clic standard se déclenche, donnez la valeur True au retour de la fonction.
Private
Function
BoutonGoogle_onclick
(
) As
Boolean
Debug.Print
"Le bouton de recherche est activé !"
BoutonGoogle_onclick =
True
End
Function
IV-D. Modifier dynamiquement une page▲
Nous allons ajouter un bouton de recherche personnalisée, en plus des deux boutons de recherche de Google.
Pour démarrer ce chapitre, on reprend le formulaire avec un bouton OpenExplorer et sa procédure sur clic.
Private
Sub
OpenExplorer_Click
(
)
Dim
oNav As
SHDocVw.InternetExplorer
Dim
oDoc As
MSHTML.HTMLDocument
Set
oNav =
New
SHDocVw.InternetExplorer
oNav.Visible
=
True
oNav.navigate
"http://google.fr"
' Attente avec timeout de 10 s
If
WaitIE
(
oNav, 10
) Then
' 10 s écoulées et page non chargée
MsgBox
"Time out!"
Else
' Page chargée, on continue
Set
oDoc =
oNav.document
End
If
End
Sub
Jetons un coup d'œil à la source ; voici ce qu'on y trouve (légèrement remis en forme) :
<span class
=
ds >
<span class
=
lsbb>
<input name
=
btnG type
=
submit value
=
"Recherche Google"
class
=
lsb>
</span>
</span>
<span class
=
ds>
<span class
=
lsbb>
<input name
=
btnI type
=
submit class
=
lsb value
=
"J'ai de la chance"
>
</span>
</span>
Dans un premier temps, notre objectif est de rajouter un troisième bouton en suivant le même modèle que les deux existants.
C'est-à-dire : un élément input dans un span lui-même dans un span.
En conservant cette même structure, on aura visuellement un bouton identique aux deux autres.
Voici le code HTML de ce que nous souhaitons ajouter :
<span class
=
ds >
<span class
=
lsbb>
<input name
=
btnA type
=
submit value
=
"Recherche Arkham46"
class
=
lsb>
</span>
</span>
Le nouveau bouton aura pour nom btnA et pour texte "Recherche Arkham46".
C'est un élément de type submit car il sert à soumettre le formulaire de recherche.
On commence par déclarer trois objets, dont deux au début de la procédure OpenExplorer_Click :
Dim
oSpan_ds As
MSHTML.HTMLSpanElement
Dim
oSpan_lsbb As
MSHTML.HTMLSpanElement
Et le troisième (le bouton) au début du module, avec WithEvents, pour avoir accès à ses événements :
Private
WithEvents oButton As
MSHTML.HTMLInputElement
On a ainsi à disposition un objet pour notre bouton et un objet pour chaque span.
Créons alors nos éléments après chargement du document.
[...
]
' Page chargée, on continue
Set
oDoc =
oNav.document
' Span
Set
oSpan_ds =
oDoc.createElement
(
"span"
)
oSpan_ds.className
=
"ds"
' Span
Set
oSpan_lsbb =
oDoc.createElement
(
"span"
)
oSpan_ds.className
=
"lsbb"
' Bouton
Set
oButton =
oDoc.createElement
(
"input"
)
oButton.Name
=
"btnA"
oButton.Type
=
"submit"
oButton.Value
=
"Recherche Arkham46"
oButton.className
=
"lsb"
End
If
End
Sub
On crée d'abord chacun des objets avec CreateElement puis on définit les attributs.
Les éléments sont créés sur l'objet Document.
À leur création ils sont orphelins.
Il faut maintenant les ajouter au bon endroit.
' Ajout du bouton dans le span lsbb
oSpan_lsbb.appendChild
oButton
' Ajout du span lsbb dans le span ds
oSpan_ds.appendChild
oSpan_lsbb
Afin d'obtenir la hiérarchie définie précédemment, on ajoute le bouton dans le deuxième span, et le deuxième span dans le premier.
Reste à ajouter l'ensemble au bon emplacement, c'est-à-dire après le deuxième bouton existant.
Pour cela on va d'abord rechercher un bouton existant.
Puis on remonte à l'élément parent (avec la méthode ParentElement) : cela nous amène au deuxième span.
On cherche alors le parent une nouvelle fois pour nous mener au premier span.
Le parent de ce dernier est alors l'élément dans lequel on doit ajouter notre élément span contenant le bouton.
' Ajout de l'ensemble
GetButton
(
oDoc,"btnG"
).parentElement.parentElement.parentElement.appendChild
oSpan_ds
On obtient alors notre troisième bouton :
L'utilisation de appendChild ajoute l'élément à la fin.
Pour insérez un élément à un emplacement défini, il faut utiliser insertBefore ; par exemple :
' Insertion de l'ensemble
GetButton
(
oDoc,"btnG"
).parentElement.parentElement.parentElement.insertBefore
_
oSpan_ds, GetButton
(
oDoc,"btnG"
).parentElement.parentElement
Pour réagir au clic sur ce bouton, choisissez dans les listes déroulantes en haut du module VBA : oButton et onclick.
Dans la procédure, écrivons le code qui remplit automatiquement la zone de recherche.
Private
Function
oButton_onclick
(
) As
Boolean
oButton.document.forms
(
"f"
).elements
(
"q"
).Value
=
"Arkham46"
oButton_onclick =
True
End
Function
Pour changer, ici on a utilisé les collections forms et elements pour trouver la zone de recherche.
Cela fonctionne correctement car il n'y a qu'un seul formulaire nommé "f", et un seul élément nommé "q".
On n'oublie pas de mettre le retour de la fonction à True pour que l'événement continue.
C'est maintenant terminé : un clic sur le nouveau bouton lance une recherche sur le mot "Arkham46".
Voici le code complet de ce formulaire :
Option
Explicit
Private
WithEvents oButton As
MSHTML.HTMLInputElement
Private
Function
oButton_onclick
(
) As
Boolean
oButton.document.forms
(
"f"
).elements
(
"q"
).Value
=
"Arkham46"
oButton_onclick =
True
End
Function
Private
Sub
OpenExplorer_Click
(
)
Dim
oNav As
SHDocVw.InternetExplorer
Dim
oDoc As
MSHTML.HTMLDocument
Dim
oSpan_ds As
MSHTML.HTMLSpanElement
Dim
oSpan_lsbb As
MSHTML.HTMLSpanElement
Set
oNav =
New
SHDocVw.InternetExplorer
oNav.Visible
=
True
oNav.navigate
"http://google.fr"
' Attente avec timeout de 10 s
If
WaitIE
(
oNav, 10
) Then
' 10 s écoulées et page non chargée
MsgBox
"Time out!"
Else
' Page chargée, on continue
Set
oDoc =
oNav.document
' Span
Set
oSpan_ds =
oDoc.createElement
(
"span"
)
oSpan_ds.className
=
"ds"
' Span
Set
oSpan_lsbb =
oDoc.createElement
(
"span"
)
oSpan_lsbb.className
=
"lsbb"
' Bouton
Set
oButton =
oDoc.createElement
(
"input"
)
oButton.Name
=
"btnA"
oButton.Type
=
"submit"
oButton.Value
=
"Recherche Arkham46"
oButton.className
=
"lsb"
' Ajout du bouton dans le span lsbb
oSpan_lsbb.appendChild
oButton
' Ajout du span lsbb dans le span ds
oSpan_ds.appendChild
oSpan_lsbb
' Ajout de l'ensemble
GetButton
(
oDoc,"btnG"
).parentElement.parentElement.parentElement.appendChild
oSpan_ds
End
If
End
Sub
' Recherche d'un bouton par son nom dans le document
Private
Function
GetButton
(
Document As
HTMLDocument, ButtonName As
String
) As
MSHTML.HTMLInputElement
If
Document.getElementsByName
(
ButtonName).length
>
1
Then
Set
GetButton =
Document.getElementsByName
(
ButtonName)(
1
)
Else
Set
GetButton =
Document.getElementsByName
(
ButtonName)(
0
)
End
If
End
Function
IV-E. Créer un gestionnaire d'événements commun▲
Maintenant, nous souhaitons réagir au clic sur chacun des trois boutons.
On pourrait déclarer trois objets au début du module, avec WithEvents, et gérer chaque événement indépendamment.
C'est une solution qui fonctionne, mais qui devient vite lourde à gérer lorsqu'on souhaite gérer de nombreux éléments.
Nous allons donc dans ce chapitre voir comment créer un gestionnaire d'événements en VBA.
Voici le code de départ de ce chapitre, dans un formulaire avec un bouton nommé OpenExplorer :
Private
Sub
OpenExplorer_Click
(
)
Dim
oNav As
SHDocVw.InternetExplorer
Dim
oDoc As
MSHTML.HTMLDocument
Dim
oSpan_ds As
MSHTML.HTMLSpanElement
Dim
oSpan_lsbb As
MSHTML.HTMLSpanElement
Dim
oButton As
MSHTML.HTMLInputElement
Set
oNav =
New
SHDocVw.InternetExplorer
oNav.Visible
=
True
oNav.navigate
"http://google.fr"
' Attente avec timeout de 10 s
If
WaitIE
(
oNav, 10
) Then
' 10 s écoulées et page non chargée
MsgBox
"Time out!"
Else
' Page chargée, on continue
Set
oDoc =
oNav.Document
' Span
Set
oSpan_ds =
oDoc.createElement
(
"span"
)
oSpan_ds.className
=
"ds"
' Span
Set
oSpan_lsbb =
oDoc.createElement
(
"span"
)
oSpan_lsbb.className
=
"lsbb"
' Bouton
Set
oButton =
oDoc.createElement
(
"input"
)
oButton.Name
=
"btnA"
oButton.Type
=
"submit"
oButton.Value
=
"Recherche Arkham46"
oButton.className
=
"lsb"
' Ajout du bouton dans le span lsbb
oSpan_lsbb.appendChild
oButton
' Ajout du span lsbb dans le span ds
oSpan_ds.appendChild
oSpan_lsbb
' Ajout de l'ensemble
GetButton
(
oDoc, "btnG"
).parentElement.parentElement.parentElement.insertBefore
_
oSpan_ds, GetButton
(
oDoc, "btnG"
).parentElement.parentElement
End
If
End
Sub
' Recherche d'un bouton par son nom dans le document
Private
Function
GetButton
(
Document As
HTMLDocument, ButtonName As
String
) As
MSHTML.HTMLInputElement
If
Document.getElementsByName
(
ButtonName).length
>
1
Then
Set
GetButton =
Document.getElementsByName
(
ButtonName)(
1
)
Else
Set
GetButton =
Document.getElementsByName
(
ButtonName)(
0
)
End
If
End
Function
Ce code ouvre Internet Explorer, navigue sur la page de recherche Google, et ajoute le bouton "Recherche Arkham46".
Par rapport au code précédent, on a supprimé la déclaration de oButton de l'en-tête pour la mettre en local dans la procédure.
Tout l'enjeu est maintenant de pouvoir gérer des événements sans déclarer chacun des éléments en en-tête de module.
Chacun des éléments possède des propriétés définissant le gestionnaire affecté à chaque élément :
Par exemple : onclick, onchange...
S'il est classique de créer un gestionnaire d'événements en JavaScript par exemple, il est moins facile de le faire en VBA.
Mais c'est tout de même possible.
Le gestionnaire est en fait un objet, qui possède une méthode par défaut.
Pour créer un tel objet, il faut ajouter un module de classe : menu Insertion => Module de classe.
Le code de ce module est le suivant :
Option
Explicit
Public
Event WebEvent
(
)
Public
Sub
EventFired
(
)
'Attribute EventFired.VB_UserMemId = 0
RaiseEvent WebEvent
End
Sub
Il contient :
- un événement WebEvent : c'est cet événement que nous allons récupérer dans notre code.
- une procédure EventFired (son nom est arbitraire) qui est la procédure exécutée lorsqu'un événement est levé.
Sauvegardez ce module, avec le nom WebEvents.
Pour une utilisation dans une application Internet Explorer, il faut que ce module soit public.
Définissez la propriété Instancing du module à 2 - PublicNotCreatable.
Le module peut rester privé si la page est affichée dans un contrôle WebBrowser.
Ensuite il faut définir la procédure EventFired comme procédure par défaut de l'objet.
Pour cela il faut exporter, modifier, puis réimporter le module.
- Exportez le module : menu Fichier => Exporter un fichier....
- Supprimez le module : menu Fichier => Supprimer WebEvents....
- Modifiez le fichier exporté dans un éditeur texte : décommentez la première ligne de la procédure.
- Importez le fichier modifié : menu Fichier => Importer un fichier....
La ligne Attribute EventFired.VB_UserMemId = 0 est prise en compte à l'import, mais n'est pas affichée dans l'éditeur VBA.
Le module WebEvents est prêt.
Pour l'utiliser, il faut déclarer dans le code du formulaire (en en-tête de module) un objet de type WebEvents.
N'oubliez pas l'instruction WithEvents qui permet d'utiliser l'événement WebEvent de l'objet.
Private
WithEvents oEvents As
WebEvents
Ensuite il faut instancier cet objet, dans la procédure OpenExplorer_Click.
En début de procédure, après les déclarations, ajoutez :
Set
oEvents =
New
WebEvents
L'objet est prêt à servir de gestionnaire d'événements.
Il suffit de l'affecter à la propriété correspondant à l'événement à gérer.
On peut réutiliser le même gestionnaire autant de fois que l'on veut.
En fin de procédure, on affecte l'objet oEvents à chacun des boutons :
[...
]
' Affecte le gestionnaire d'événements aux boutons
GetButton
(
oDoc, "btnG"
).onclick
=
oEvents
GetButton
(
oDoc, "btnI"
).onclick
=
oEvents
oButton.onclick
=
oEvents
End
If
End
Sub
Notez que pour le bouton personnalisé, on utilise directement la variable oButton car ce bouton ajouté dynamiquement n'est pas retrouvé par la méthode getElementsByName.
Par ce code, on demande à ce qu'un clic sur un bouton déclenche la procédure par défaut de l'objet oEvents.
Cette procédure déclenche alors l'événement WebEvent que nous allons gérer.
Cliquez en haut de la page de code sur oEvents à gauche et sur WebEvent à droite.
La procédure événementielle est générée :
Private
Sub
oEvents_WebEvent
(
)
End
Sub
Écrivons-y une instruction Stop pour tester.
Affichez le formulaire et cliquez sur son bouton OpenExplorer pour ouvrir notre explorateur.
Un clic sur un des boutons nous amène à la procédure oEvents_WebEvent.
Intéressant mais comment savoir quel bouton a été cliqué ?
Pendant la durée d'un événement, l'objet Window du document HTML possède une propriété Event qui contient les informations de l'événement.
Pour pouvoir lire cette propriété il nous faut l'objet document.
Cet objet est l'objet oNav qui est déclaré dans la procédure OpenExplorer_Click et n'est donc pas disponible dans la procédure oEvents_WebEvent.
Déplacez donc sa déclaration en en-tête de module, après la déclaration de oEvents :
Private
WithEvents oEvents As
WebEvents
Private
oDoc As
MSHTML.HTMLDocument
oDoc est maintenant accessible dans toutes les procédures du module.
Jetons un œil sur les informations de l'événement.
Toujours avec le point d'arrêt dans oEvents_WebEvent, lancez le navigateur et cliquez sur un bouton.
Revenez à l'éditeur VBA et affichez la fenêtre Variables locales : menu Affichage => Fenêtre Variables Locales.
Naviguez dans l'arborescence : Me => oDoc => parentWindow => event.
Nous constatons que cet objet est de type IHTMLEventObj.
Il contient de nombreuses informations dont l'objet source de l'événement : srcElement.
On voit également le type d'événement ("click") qui pourrait permettre de gérer plusieurs types d'événements dans la procédure.
Dans notre procédure événementielle, on peut alors récupérer l'objet event.
Private
Sub
oEvents_WebEvent
(
)
Dim
oEvent As
IHTMLEventObj
Set
oEvent =
oDoc.parentWindow.event
End
Sub
Puis on ajoute du code de traitement de l'événement :
Private
Sub
oEvents_WebEvent
(
)
Dim
oEvent As
IHTMLEventObj
Set
oEvent =
oDoc.parentWindow.event
' Traitement fonction du nom de l'élément
Select
Case
oEvent.srcElement.getAttribute
(
"name"
)
Case
"btnA"
' Bouton "Recherche Arkham46"
' Inscrit la valeur recherchée
oEvent.srcElement.Document.getElementsByName
(
"q"
)(
0
).Value
=
"Arkham46"
Case
"btnG"
' Bouton "Recherche Google"
'
Case
"btnI"
' Bouton "J'ai de la chance"
' Désactive ce bouton
oEvent.returnValue
=
False
End
Select
' Ajout du texte du bouton dans la fenêtre exécution
Debug.Print
"Click sur "
&
oEvent.srcElement.getAttribute
(
"value"
)
End
Sub
On récupère les attributs HTML de l'élément avec la méthode getAttribute :
- name est le nom du bouton ;
- value est le texte du bouton.
Si le nom du bouton est "btnA", on écrit le texte de recherche prédéfini "Arkham46".
Pour le bouton "btnI" (bouton 'J'ai de la chance'), on désactive l'événement en donnant la valeur False à returnValue.
Pour le bouton "btnG" (bouton de recherche classique) on ne fait rien de particulier.
On écrit également le texte du bouton cliqué dans la fenêtre Exécution (CTRL+G).
C'est maintenant terminé : le clic sur chacun des boutons est géré dans une seule procédure.
Voici le code complet de ce formulaire :
Option
Explicit
Private
WithEvents oEvents As
WebEvents
Private
oDoc As
MSHTML.HTMLDocument
Private
Sub
oEvents_WebEvent
(
)
Dim
oEvent As
IHTMLEventObj
Set
oEvent =
oDoc.parentWindow.event
' Traitement fonction du nom de l'élément
Select
Case
oEvent.srcElement.getAttribute
(
"name"
)
Case
"btnA"
' Bouton "Recherche Arkham46"
' Inscrit la valeur recherchée
oEvent.srcElement.Document.getElementsByName
(
"q"
)(
0
).Value
=
"Arkham46"
Case
"btnG"
' Bouton "Recherche Google"
'
Case
"btnI"
' Bouton "J'ai de la chance"
' Désactive ce bouton
oEvent.returnValue
=
False
End
Select
' Ajout du texte du bouton dans la fenêtre exécution
Debug.Print
"Click sur "
&
oEvent.srcElement.getAttribute
(
"value"
)
End
Sub
Private
Sub
OpenExplorer_Click
(
)
Dim
oNav As
SHDocVw.InternetExplorer
Dim
oSpan_ds As
MSHTML.HTMLSpanElement
Dim
oSpan_lsbb As
MSHTML.HTMLSpanElement
Dim
oButton As
MSHTML.HTMLInputElement
Set
oEvents =
New
WebEvents
Set
oNav =
New
SHDocVw.InternetExplorer
oNav.Visible
=
True
oNav.navigate
"http://google.fr"
' Attente avec timeout de 10 s
If
WaitIE
(
oNav, 10
) Then
' 10 s écoulées et page non chargée
MsgBox
"Time out!"
Else
' Page chargée, on continue
Set
oDoc =
oNav.Document
' Span
Set
oSpan_ds =
oDoc.createElement
(
"span"
)
oSpan_ds.className
=
"ds"
' Span
Set
oSpan_lsbb =
oDoc.createElement
(
"span"
)
oSpan_lsbb.className
=
"lsbb"
' Bouton
Set
oButton =
oDoc.createElement
(
"input"
)
oButton.Name
=
"btnA"
oButton.Type
=
"submit"
oButton.Value
=
"Recherche Arkham46"
oButton.className
=
"lsb"
' Ajout du bouton dans le span lsbb
oSpan_lsbb.appendChild
oButton
' Ajout du span lsbb dans le span ds
oSpan_ds.appendChild
oSpan_lsbb
' Ajout de l'ensemble
GetButton
(
oDoc, "btnG"
).parentElement.parentElement.parentElement.insertBefore
_
oSpan_ds, GetButton
(
oDoc, "btnG"
).parentElement.parentElement
' Affecte le gestionnaire d'événements aux boutons
GetButton
(
oDoc, "btnG"
).onclick
=
oEvents
GetButton
(
oDoc, "btnI"
).onclick
=
oEvents
oButton.onclick
=
oEvents
End
If
End
Sub
' Recherche d'un bouton par son nom dans le document
Private
Function
GetButton
(
Document As
HTMLDocument, ButtonName As
String
) As
MSHTML.HTMLInputElement
If
Document.getElementsByName
(
ButtonName).length
>
1
Then
Set
GetButton =
Document.getElementsByName
(
ButtonName)(
1
)
Else
Set
GetButton =
Document.getElementsByName
(
ButtonName)(
0
)
End
If
End
Function