diff --git a/index.html b/index.html
index c7d89f7c331f0e6412fde5c659215e78468a9c4a..4782e2e9b66de1191d77710e078ccabb3392be01 100644
--- a/index.html
+++ b/index.html
@@ -34,6 +34,8 @@
 				<li><a href="/help" class="helpLink">Support</a></li>
 			</ul>
 		</nav>
+		<div class="testdiv"><button class="test">test1</button><button class="test">test2</button><button class="test">test3</button></div>
+		<div class="testdiv2"><button class="test2">test1</button><button class="test2">test2</button><button class="test2">test3</button></div>
 	</header>
 	<section class="viewContainer">
 		<header class="viewTitle"></header>
diff --git a/package.json b/package.json
index 0eaac26440b030a80f378c1a853852d29741b951..cc8d4bb13d332bd02b23301feb8a40e19df2de60 100644
--- a/package.json
+++ b/package.json
@@ -7,7 +7,7 @@
     "test": "echo \"Error: no test specified\" && exit 1",
     "build": "webpack --mode=production",
     "watch": "webpack --mode=development --watch",
-	"start": "webpack serve --mode=development"
+    "start": "webpack serve --mode=development"
   },
   "author": "Thomas Fritsch <thomas.fritsch@univ-lille.fr> (https://gitlab.univ-lille.fr/thomas.fritsch)",
   "homepage": "https://gitlab.univ-lille.fr/js",
diff --git a/src/AboutView.js b/src/AboutView.js
index 03c1bcf06f0c44c6764c14fbda4f3d43ce4238c8..e4fe79aeb8f28c337028c2402fb5a96d88b4e53a 100644
--- a/src/AboutView.js
+++ b/src/AboutView.js
@@ -1,5 +1,6 @@
 import Router from './Router.js';
 import View from './View.js';
+import $ from './lib/jqlite.js';
 
 /**
  * Classe de la page "A propos"
@@ -8,6 +9,7 @@ import View from './View.js';
 export default class AboutView extends View {
 	show() {
 		super.show();
+		this.element.addClass('is-loading');
 		// quand on affiche la page, on charge le fichier `about.html`
 		// on pourrait optimiser ici le fonctionnement en ne le chargeant qu'une seule fois
 		// (pour le moment à chaque fois qu'on affiche la view, une nouvelle requête AJAX est déclenchée)
@@ -24,12 +26,16 @@ export default class AboutView extends View {
 	 * @see #handleButtonClick
 	 */
 	showFileContent(html) {
-		this.element.innerHTML = html;
+		this.element.html(html);
+		this.element.removeClass('is-loading');
 		// l'ajout d'écouteur d'événement ne peut se faire qu'une fois que la page HTML a été modifiée
 		// (sinon le bouton n'existe pas encore)
+		// this.element
+		// 	.querySelector('.button')
+		// 	.addEventListener('click', event => this.handleButtonClick(event));
 		this.element
-			.querySelector('.button')
-			.addEventListener('click', event => this.handleButtonClick(event));
+			.find('.button')
+			.on('click', event => this.handleButtonClick(event));
 	}
 	/**
 	 * Méthode déclenchée par le click sur le bouton "Nous contacter"
diff --git a/src/GameDetailView.js b/src/GameDetailView.js
index 40f329099f360bc2d7ee47a34621b981ab226771..d5b63ea0b90af28a1084a903f2afc71eeae7ce93 100644
--- a/src/GameDetailView.js
+++ b/src/GameDetailView.js
@@ -1,6 +1,7 @@
 import { API_KEY } from './config.js';
 import Router from './Router.js';
 import View from './View.js';
+import $ from './lib/jqlite.js';
 
 export default class GameDetailView extends View {
 	game;
@@ -13,7 +14,8 @@ export default class GameDetailView extends View {
 	show(slug) {
 		super.show();
 		// ajout de l'état "loading"
-		this.element.classList.add('is-loading');
+		$(this.element).addClass('is-loading');
+		//this.element.classList.add('is-loading');
 		// création des URL de l'API pour le détail et les screenshots
 		const detailApiUrl =
 				`https://api.rawg.io/api/games/` + encodeURIComponent(slug),
@@ -28,7 +30,7 @@ export default class GameDetailView extends View {
 			.then(data => {
 				this.screenshots = data;
 				this.render();
-				this.element.classList.remove('is-loading');
+				$(this.element).removeClass('is-loading');
 			});
 	}
 
@@ -51,7 +53,7 @@ export default class GameDetailView extends View {
 		} = this.game;
 		const releasedDate = new Date(released);
 		// rendu dans la vue
-		this.element.innerHTML = /*html*/ `
+		$(this.element).html(`
 			<div class="backgroundImage">
 				<img src="${background_image}" />
 			</div>
@@ -91,14 +93,20 @@ export default class GameDetailView extends View {
 				</div>
 			</section>
 			<section class="description">${description}</section>
-			`;
+			`);
 
 		// écoute du clic sur les liens du "fil d'Ariane" pour éviter le rechargement de page
-		this.element.querySelectorAll('.breadcrumb a').forEach(link =>
-			link.addEventListener('click', event => {
+		$(this.element)
+			.find('.breadcrumb a')
+			.on('click', event => {
 				event.preventDefault();
 				Router.navigate(event.currentTarget.getAttribute('href'));
-			})
-		);
+			});
+		// this.element.querySelectorAll('.breadcrumb a').forEach(link =>
+		// 	link.addEventListener('click', event => {
+		// 		event.preventDefault();
+		// 		Router.navigate(event.currentTarget.getAttribute('href'));
+		// 	})
+		// );
 	}
 }
diff --git a/src/GameListView.js b/src/GameListView.js
index 378783e35d7743ed74d7177d4638565db618567e..ad9f1bf82533cb6c6e80c83acb5df8beb7d10832 100644
--- a/src/GameListView.js
+++ b/src/GameListView.js
@@ -2,6 +2,7 @@ import { API_KEY } from './config.js';
 import renderGameThumbnail from './renderGameThumbnail.js';
 import Router from './Router.js';
 import View from './View.js';
+import $ from './lib/jqlite.js';
 
 export default class GameListView extends View {
 	searchForm;
@@ -10,13 +11,15 @@ export default class GameListView extends View {
 	constructor(element) {
 		super(element);
 		// détection du clic sur le bouton "loupe" pour afficher/masquer le form de recherche
-		this.toggleSearchButton = this.element.querySelector('.toggleSearchButton');
-		this.toggleSearchButton.addEventListener('click', event =>
+		// this.toggleSearchButton = this.element.querySelector('.toggleSearchButton');
+		this.toggleSearchButton = $(this.element).find('.toggleSearchButton');
+		$(this.toggleSearchButton).on('click', event =>
 			this.toggleSearchForm(event)
 		);
 		// détection de la soumission du formulaire de recherche
-		this.searchForm = this.element.querySelector('.searchForm');
-		this.searchForm.addEventListener('submit', event =>
+		//this.searchForm = this.element.querySelector('.searchForm');
+		this.searchForm = $(this.element).find('.searchForm');
+		$(this.searchForm).on('submit', event =>
 			this.handleSearchFormSubmit(event)
 		);
 	}
@@ -36,9 +39,11 @@ export default class GameListView extends View {
 	 * @param {string} ordering ordre d'affichage des résultats
 	 */
 	renderGameList(search = '', ordering) {
-		this.element.querySelector('.results').classList.add('is-loading');
-		this.searchForm.querySelector('button').disabled = true;
-		this.searchForm.querySelector('button').setAttribute('disabled', true);
+		// this.element.querySelector('.results').classList.add('is-loading');
+		// this.searchForm.querySelector('button').disabled = true;
+		// this.searchForm.querySelector('button').setAttribute('disabled', true);
+		$(this.element).find('.results').addClass('is-loading');
+		$(this.searchForm.find('button')).attr('disabled', true);
 		fetch(
 			`https://api.rawg.io/api/games?search=${encodeURIComponent(
 				search
@@ -49,19 +54,28 @@ export default class GameListView extends View {
 				// rendu de la liste des jeux
 				let html = '';
 				data.results.forEach(game => (html += renderGameThumbnail(game)));
-				this.element.querySelector('.results').innerHTML = html;
-				// suppression du "loader" et réactivation du formulaire
-				this.element.querySelector('.results').classList.remove('is-loading');
-				this.searchForm.querySelector('button').disabled = false;
+				// this.element.querySelector('.results').innerHTML = html;
+				// // suppression du "loader" et réactivation du formulaire
+				// this.element.querySelector('.results').classList.remove('is-loading');
+				// this.searchForm.querySelector('button').disabled = false;
+				$(this.element).find('.results').html(html);
+				$(this.element).find('.results').removeClass('is-loading');
+				$(this.searchForm.find('button')).removeAttr('disabled');
 				// détection du clic sur les vignettes de jeu
 				// pour navigation vers la page détail (sans rechargement de page)
-				const gameLinks = this.element.querySelectorAll('.results > a');
-				gameLinks.forEach(gameLink =>
-					gameLink.addEventListener('click', event => {
+				// const gameLinks = this.element.querySelectorAll('.results > a');
+				// gameLinks.forEach(gameLink =>
+				// 	gameLink.addEventListener('click', event => {
+				// 		event.preventDefault();
+				// 		Router.navigate(gameLink.getAttribute('href'));
+				// 	})
+				// );
+				$(this.element)
+					.find('.results > a')
+					.on('click', event => {
 						event.preventDefault();
-						Router.navigate(gameLink.getAttribute('href'));
-					})
-				);
+						Router.navigate(event.currentTarget.getAttribute('href'));
+					});
 			});
 	}
 	/**
@@ -69,13 +83,13 @@ export default class GameListView extends View {
 	 * @param {Event} event événement déclenché par le bouton sur lequel on a cliqué
 	 */
 	toggleSearchForm(event) {
-		const isOpened = this.searchForm.getAttribute('style') !== 'display: none;';
+		const isOpened = $(this.searchForm).attr('style') !== 'display: none;';
 		if (!isOpened) {
-			this.searchForm.setAttribute('style', '');
-			this.toggleSearchButton.classList.add('opened');
+			$(this.searchForm).attr('style', '');
+			$(this.toggleSearchButton).addClass('opened');
 		} else {
-			this.searchForm.setAttribute('style', 'display: none;');
-			this.toggleSearchButton.classList.remove('opened');
+			$(this.searchForm).attr('style', 'display: none;');
+			$(this.toggleSearchButton).removeClass('opened');
 		}
 	}
 	/**
@@ -86,8 +100,8 @@ export default class GameListView extends View {
 	 */
 	handleSearchFormSubmit(event) {
 		event.preventDefault();
-		const searchInput = this.searchForm.querySelector('[name=search]'),
-			orderingSelect = this.searchForm.querySelector('[name=ordering]');
-		this.renderGameList(searchInput.value, orderingSelect.value);
+		const searchInput = $(this.searchForm).find('[name=search]'),
+			orderingSelect = $(this.searchForm).find('[name=ordering]');
+		this.renderGameList(searchInput.val(), orderingSelect.val());
 	}
 }
diff --git a/src/HelpView.js b/src/HelpView.js
index 898adedef7fbfd2c93866c9695961c5c1b4e3a88..be02c5b5615acf4faa4d72534ffd14fd8ccb11d8 100644
--- a/src/HelpView.js
+++ b/src/HelpView.js
@@ -1,4 +1,5 @@
 import View from './View.js';
+import $ from './lib/jqlite.js';
 
 export default class HelpView extends View {
 	/**
@@ -9,9 +10,9 @@ export default class HelpView extends View {
 	constructor(element) {
 		super(element);
 		// détection du submit du formulaire
-		this.element
-			.querySelector('.helpForm')
-			.addEventListener('submit', event => this.handleSubmit(event));
+		$(this.element)
+			.find('.helpForm')
+			.on('submit', event => this.handleSubmit(event));
 	}
 
 	/**
@@ -23,11 +24,11 @@ export default class HelpView extends View {
 	handleSubmit(event) {
 		event.preventDefault();
 		// récupération des 2 champs du formulaire
-		const subjectInput = this.element.querySelector('input[name=subject]');
-		const bodyTextarea = this.element.querySelector('textarea[name=body]');
+		const subjectInput = $(this.element).find('input[name=subject]');
+		const bodyTextarea = $(this.element).find('textarea[name=body]');
 		// récupération du texte saisi par l'utilisateur.rice
-		const subject = subjectInput.value,
-			body = bodyTextarea.value;
+		const subject = subjectInput.val(),
+			body = bodyTextarea.val();
 		// vérification des champs obligatoires
 		if (subject === '') {
 			alert('le champ "SUJET" est obligatoire');
@@ -42,8 +43,8 @@ export default class HelpView extends View {
 			subject
 		)}&body=${encodeURIComponent(body)}`;
 		// on vide les champs
-		subjectInput.value = '';
-		bodyTextarea.value = '';
+		subjectInput.val('');
+		bodyTextarea.val('');
 		// on aurait aussi pu faire : form.reset();
 	}
-}
\ No newline at end of file
+}
diff --git a/src/Router.js b/src/Router.js
index 08ec59c339a526b7dbc5e1c278d6a5ed6699d30e..a6c66378a0a3568b70e80cff7a0e60fbabb6796d 100644
--- a/src/Router.js
+++ b/src/Router.js
@@ -1,3 +1,4 @@
+import $ from './lib/jqlite.js';
 /**
  * Classe Router qui permet de gérer la navigation dans l'application sans rechargement de page.
  * (Single Page Application)
@@ -23,15 +24,23 @@ export default class Router {
 	static setMenuElement(menuElement) {
 		this.#menuElement = menuElement;
 		// on écoute le clic sur tous les liens du menu
-		const menuLinks = this.#menuElement.querySelectorAll('a');
-		menuLinks.forEach(link =>
-			link.addEventListener('click', event => {
-				event.preventDefault();
-				// on récupère le href du lien cliqué pour déclencher navigate(...)
-				const linkHref = event.currentTarget.getAttribute('href');
-				Router.navigate(linkHref);
-			})
-		);
+		// const menuLinks = this.#menuElement.querySelectorAll('a');
+		// menuLinks.forEach(link =>
+		// 	link.addEventListener('click', event => {
+		// 		event.preventDefault();
+		// 		// on récupère le href du lien cliqué pour déclencher navigate(...)
+		// 		const linkHref = event.currentTarget.getAttribute('href');
+		// 		Router.navigate(linkHref);
+		// 	})
+		// );
+
+		const $menuLinks = $(this.#menuElement).find('a');
+		$menuLinks.on('click', event => {
+			event.preventDefault();
+			// on récupère le href du lien cliqué pour déclencher navigate(...)
+			const linkHref = $(event.currentTarget).attr('href');
+			Router.navigate(linkHref);
+		});
 	}
 	/**
 	 * Affiche la view correspondant à `path` dans le tableau `routes`
@@ -70,13 +79,14 @@ export default class Router {
 				viewParam = pathEnd;
 			}
 			route.view.show(viewParam);
-			this.titleElement.innerHTML = `<h1>${route.title}</h1>`;
+			$(this.titleElement).html(`<h1>${route.title}</h1>`);
 
 			// Activation/désactivation des liens du menu
-			const previousMenuLink = this.#menuElement.querySelector('a.active'),
-				newMenuLink = this.#menuElement.querySelector(`a[href="${path}"]`);
-			previousMenuLink?.classList.remove('active'); // on retire la classe "active" du précédent menu
-			newMenuLink?.classList.add('active'); // on ajoute la classe CSS "active" sur le nouveau lien
+			const $previousMenuLink = $(this.#menuElement).find('a.active'),
+				$newMenuLink = $(this.#menuElement).find(`a[href="${path}"]`);
+
+			$previousMenuLink.removeClass('active'); // on retire la classe "active" du précédent menu
+			$newMenuLink.addClass('active'); // on ajoute la classe CSS "active" sur le nouveau lien
 
 			// History API : ajout d'une entrée dans l'historique du navigateur
 			// pour pouvoir utiliser les boutons précédent/suivant
diff --git a/src/View.js b/src/View.js
index 2c0a338f58a92a3d0cee0dcebf11052f5d8058ca..6041ddbc5bdbfdd193c92c76ef25cfac0c141882 100644
--- a/src/View.js
+++ b/src/View.js
@@ -1,3 +1,4 @@
+import $ from './lib/jqlite.js';
 /**
  * Classe de base des vues de notre application.
  * Permet d'associer une balise HTML à la vue et de l'afficher/masquer.
@@ -15,12 +16,12 @@ export default class View {
 	 * Affiche la vue en lui ajoutant la classe CSS `active`
 	 */
 	show() {
-		this.element.classList.add('active');
+		$(this.element).addClass('active');
 	}
 	/**
 	 * Masque la vue en enlevant la classe CSS `active`
 	 */
 	hide() {
-		this.element.classList.remove('active');
+		$(this.element).removeClass('active');
 	}
 }
diff --git a/src/lib/JQObject.js b/src/lib/JQObject.js
new file mode 100644
index 0000000000000000000000000000000000000000..72158fdeff07bac90e8c9923855295bfd023e5fd
--- /dev/null
+++ b/src/lib/JQObject.js
@@ -0,0 +1,76 @@
+import $ from './jqlite.js';
+
+export default class JQObject {
+	constructor(DOMElements) {
+		let i = 0;
+		DOMElements.forEach(Element => {
+			this[i] = Element;
+			i++;
+		});
+		this.length = i;
+	}
+
+	html(newHTML) {
+		for (let i = 0; i < this.length; i++) {
+			this[i].innerHTML = newHTML;
+		}
+		return this;
+	}
+
+	append(newHTML) {
+		return this.html(this.html() + newHTML);
+	}
+
+	find(querySelector) {
+		return $(querySelector, this[0]);
+	}
+
+	addClass(className) {
+		for (let i = 0; i < this.length; i++) {
+			this[i].classList.add(className);
+		}
+		return this;
+	}
+
+	removeClass(className) {
+		for (let i = 0; i < this.length; i++) {
+			this[i].classList.remove(className);
+		}
+		return this;
+	}
+
+	attr(attributeName, newValue = null) {
+		if (newValue != null) {
+			for (let i = 0; i < this.length; i++) {
+				this[i].setAttribute(attributeName, newValue);
+			}
+			return this;
+		}
+
+		return this[0].getAttribute(attributeName);
+	}
+
+	removeAttr(attributeName) {
+		for (let i = 0; i < this.length; i++) {
+			this[i].removeAttribute(attributeName);
+		}
+		return this;
+	}
+
+	val(newValue = null) {
+		if (newValue) {
+			for (let i = 0; i < this.length; i++) {
+				this[i].value = newValue;
+			}
+			return this;
+		}
+		return this[0].value;
+	}
+
+	on(eventName, eventHandler) {
+		if (this[0]) {
+			this[0].addEventListener(eventName, eventHandler);
+		}
+		return this;
+	}
+}
diff --git a/src/lib/jqlite.js b/src/lib/jqlite.js
new file mode 100644
index 0000000000000000000000000000000000000000..077cce546a7c4a8180b8f06b2ca2b02c18d83dad
--- /dev/null
+++ b/src/lib/jqlite.js
@@ -0,0 +1,15 @@
+import JQObject from './JQObject.js';
+
+export default function $(querySelector, element = document) {
+	if (typeof querySelector === 'object') {
+		if (querySelector instanceof JQObject) {
+			return querySelector;
+		}
+		querySelector = querySelector.className;
+		querySelector = querySelector
+			.split(' ')
+			.map(className => '.' + className)
+			.join(' ');
+	}
+	return new JQObject(element.querySelectorAll(querySelector));
+}
diff --git a/src/main.js b/src/main.js
index dd4916d4bc5db1996e0141254249989c87cf3b2a..f93fd52616d1eda496286383a3e3d74644ededc1 100644
--- a/src/main.js
+++ b/src/main.js
@@ -3,22 +3,31 @@ import GameDetailView from './GameDetailView.js';
 import GameListView from './GameListView.js';
 import HelpView from './HelpView.js';
 import Router from './Router.js';
+import $ from './lib/jqlite.js';
+
+$('.logo').on('click', event => {
+	event.preventDefault();
+	Router.navigate('/', true);
+});
+
+$('body > footer a').on('mouseover', event => {
+	$(event.currentTarget).addClass('mouseover');
+});
+
+$('body > footer a').on('mouseout', event => {
+	$(event.currentTarget).removeClass('mouseover');
+});
 
 // Modification du footer
-document.querySelector('body > footer > div:nth-of-type(2)').innerHTML +=
-	' / CSS inspirée de <a href="https://store.steampowered.com/">steam</a>';
+$('body > footer > div:nth-of-type(2)').append(
+	' / CSS inspirée de <a href="https://store.steampowered.com/">steam</a>'
+);
 
 // création des vues de notre application
-const helpView = new HelpView(document.querySelector('.viewContent .help'));
-const gameListView = new GameListView(
-	document.querySelector('.viewContent > .gameList')
-);
-const aboutView = new AboutView(
-	document.querySelector('.viewContent > .about')
-);
-const gameDetailView = new GameDetailView(
-	document.querySelector('.viewContent > .gameDetail')
-);
+const helpView = new HelpView($('.viewContent .help'));
+const gameListView = new GameListView($('.viewContent > .gameList'));
+const aboutView = new AboutView($('.viewContent > .about'));
+const gameDetailView = new GameDetailView($('.viewContent > .gameDetail'));
 
 // mise en place du Router
 const routes = [
@@ -29,11 +38,19 @@ const routes = [
 ];
 Router.routes = routes;
 // élément dans lequel afficher le <h1> de la vue
-Router.titleElement = document.querySelector('.viewTitle');
+Router.titleElement = $('.viewTitle');
 // gestion des liens du menu (détection du clic et activation/désactivation)
-Router.setMenuElement(document.querySelector('.mainMenu'));
+Router.setMenuElement($('.mainMenu'));
 
 // chargement de la vue initiale selon l'URL demandée par l'utilisateur.rice (Deep linking)
 Router.navigate(window.location.pathname, true);
 // gestion des boutons précédent/suivant du navigateur (History API)
 window.onpopstate = () => Router.navigate(document.location.pathname, true);
+
+// console.log($('.logo span').html('<em>jquery</em>steam'));
+// console.log($('a').html('<em>jquery</em>steam'));
+
+// import $$ from './lib/jqlite.js';
+
+// $('.testdiv').find('.test').attr('test', 'hello');
+// $$('.testdiv2').find('.test2').attr('test', 'hello');