Avertissement Darwinien
Ce module étend les prototypes natifs de JavaScript (Storage et Element).
- Si on ne sait pas ce que ça veut dire → Google est un ami (des fois…)
- Si on sait mais que ça te trigger → Java c'est bien auss…
- Évolution ou extinction. Il faut choisir.
Installation
Coller le fichier en tête de tes pages HTML :
<script src="extends-prototypes.js"></script>
Ou à importer dans bundle si on est fancy :
import './extends-prototypes.js';
Le fichier, donc.
Taille brute : 10,89 kilo-octets (~3,54 kilo-octets gzip)
/**
* ⚠️ UTILITAIRES DE GROS FLEMMARD
*
* Parce que écrire `.removeItem()` en boucle, c'est pour les gens qui ont du temps.
* Et que les gens qui ont du temps n'existent pas.
*
* @module LazyAsFuckUtils
* @author Un développeur qui en a marre de faire des copier-coller
* @version 2.0.0-flemme-ultime
* @license DWTFYWPL - Do What The Fuck You Want to Public License
*/
(() => {
'use strict';
// =============================================================================
// STORAGE PROTOTYPES
// =============================================================================
/**
* Supprime plusieurs items du storage d'un coup
* Parce que faire une boucle manuellement, c'est 2010.
*
* @function Storage#removeItems
* @param {...string} keys - Les clés à dégager (RIP aux données)
* @returns {void} Rien. Que du vide. Comme ton frigo un dimanche soir.
*
* @example
* // Avant (pour les masochistes) :
* localStorage.removeItem('user');
* localStorage.removeItem('token');
* localStorage.removeItem('darkSecrets');
*
* // Après (pour les flemmards) :
* localStorage.removeItems('user', 'token', 'darkSecrets');
*/
Storage.prototype.removeItems ??= function (...keys) {
keys.forEach(key => this.removeItem(key));
};
/**
* Balance plusieurs items dans le storage comme un pro
* Le spread operator mais pour le localStorage. Ouais.
*
* @function Storage#setItems
* @param {Object} object - Un objet avec tes clés/valeurs (les valeurs null sont ignorées parce qu'on n'est pas des sauvages)
* @returns {void} Toujours rien. On n'est pas là pour discuter.
*
* @example
* // Fini le copier-coller à la chaîne :
* localStorage.setItems({
* user: 'TaMamie',
* theme: 'dark', // évidemment
* ieeeeSucksAss: 'true',
* thisIsUseless: null // sera ignoré, comme les résolutions du nouvel an
* });
*/
Storage.prototype.setItems ??= function (object) {
Object.keys(object).forEach(key => {
object[key] !== null && this.setItem(key, object[key]);
});
};
// =============================================================================
// ELEMENT PROTOTYPES
// =============================================================================
/**
* Colle plusieurs attributs sur un élément en une fois
* setAttribute() mais en mode bulk. Amazon Prime de l'attribut.
*
* @function Element#setAttributes
* @param {Object.<string, string|null>} object - Tes attributs en vrac (null = ignoré)
* @returns {void} Le néant. L'abysse. Tu connais la chanson.
*
* @example
* const img = document.querySelector('img');
* img.setAttributes({
* src: 'cat.jpg',
* alt: 'Un chat parce que internet',
* loading: 'lazy', // évidemment, on est des fainéants
* 'data-useless': 'yep'
* });
*/
Element.prototype.setAttributes ??= function (object) {
Object.keys(object).forEach((property) => {
object[property] !== null && this.setAttribute(property, object[property]);
});
};
/**
* Dégage plusieurs attributs d'un élément
* La Marie Kondō des attributs HTML : "Does it spark joy ? Non ? Poubelle."
*
* @function Element#removeAttributes
* @param {...string} attributes - Les attributs à guillotiner
* @returns {void} Rien, encore et toujours. C'est la marque de fabrique.
*
* @example
* const button = document.querySelector('button');
* button.removeAttributes('disabled', 'aria-hidden', 'data-whatever');
* // Bye bye les attributs. Vous ne manquerez à personne.
*/
Element.prototype.removeAttributes ??= function (...attributes) {
attributes.forEach(attr => this.removeAttribute(attr));
};
// On est sympa, on met aussi des trucs rigolos et bien pratiques…
// =============================================================================
// STRING PROTOTYPES
// =============================================================================
/**
* Capitalise la première lettre
* Parce que CSS text-transform c'est bien, mais parfois tu veux juste la première lettre.
*
* @function String#capitalize
* @returns {string} La chaîne avec la première lettre en majuscule
*
* @example
* "bonjour".capitalize(); // → "Bonjour"
*/
String.prototype.capitalize ??= function () {
return this.charAt(0).toUpperCase() + this.slice(1);
};
/**
* Tronque une chaîne avec des points de suspension
* Le "read more" des pauvres.
*
* @function String#truncate
* @param {number} length - Longueur max
* @param {string} [suffix='...'] - Ce qu'on colle à la fin
* @returns {string} La chaîne tronquée ou intacte
*
* @example
* "Un texte beaucoup trop long".truncate(10); // → "Un texte b..."
* "Court".truncate(10); // → "Court"
*/
String.prototype.truncate ??= function (length, suffix = '...') {
return this.length > length ? this.slice(0, length) + suffix : this.toString();
};
/**
* Vire tous les tags HTML
* Parce que parfois tu veux juste le texte, bordel.
*
* @function String#stripTags
* @returns {string} Le texte sans HTML
*
* @example
* "<p>Hello <strong>world</strong></p>".stripTags(); // → "Hello world"
*/
String.prototype.stripTags ??= function () {
return this.replace(/<[^>]*>/g, '');
};
/**
* Transforme en slug URL-friendly
* Ton titre d'article → une URL propre
*
* @function String#slugify
* @returns {string} Le slug tout beau tout propre
*
* @example
* "Les Événements de l'Été 2024 !".slugify();
* // → "les-evenements-de-lete-2024"
*/
String.prototype.slugify ??= function () {
return this.toLowerCase()
.normalize('NFD')
.replace(/[\u0300-\u036f]/g, '')
.replace(/[^\w\s-]/g, '')
.replace(/[\s_-]+/g, '-')
.replace(/^-+|-+$/g, '');
};
// =============================================================================
// ARRAY PROTOTYPES
// =============================================================================
/**
* Mélange un tableau (Fisher-Yates)
* Pour quand t'as besoin de random mais en vrai random.
*
* @function Array#shuffle
* @returns {Array} Un nouveau tableau mélangé
*
* @example
* [1, 2, 3, 4, 5].shuffle(); // → [3, 1, 5, 2, 4]
*/
Array.prototype.shuffle ??= function () {
const arr = [...this];
for (let i = arr.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[arr[i], arr[j]] = [arr[j], arr[i]];
}
return arr;
};
/**
* Découpe en sous-tableaux
* Pagination de pauvre, mais efficace.
*
* @function Array#chunk
* @param {number} size - Taille de chaque chunk
* @returns {Array} Tableau de tableaux
*
* @example
* [1, 2, 3, 4, 5].chunk(2); // → [[1, 2], [3, 4], [5]]
*/
Array.prototype.chunk ??= function (size) {
const result = [];
for (let i = 0; i < this.length; i += size) {
result.push(this.slice(i, i + size));
}
return result;
};
/**
* Déduplique un tableau
* Set mais en plus simple à écrire.
*
* @function Array#unique
* @returns {Array} Tableau sans doublons
*
* @example
* [1, 2, 2, 3, 3, 3].unique(); // → [1, 2, 3]
*/
Array.prototype.unique ??= function () {
return [...new Set(this)];
};
/**
* Récupère le dernier élément
* Parce que [arr.length - 1] c'est moche.
*
* @function Array#last
* @returns {*} Le dernier élément
*
* @example
* [1, 2, 3].last(); // → 3
*/
Array.prototype.last ??= function () {
return this[this.length - 1];
};
/**
* Supprime des items du tableau
* Filter mais en plus intuitif.
*
* @function Array#remove
* @param {...*} items - Les items à dégager
* @returns {Array} Nouveau tableau sans ces items
*
* @example
* [1, 2, 3, 4].remove(2, 4); // → [1, 3]
*/
Array.prototype.remove ??= function (...items) {
return this.filter(item => !items.includes(item));
};
// =============================================================================
// NUMBER PROTOTYPES
// =============================================================================
/**
* Limite un nombre entre min et max
* Math.min(Math.max()) mais lisible.
*
* @function Number#clamp
* @param {number} min - Valeur min
* @param {number} max - Valeur max
* @returns {number} Le nombre clampé
*
* @example
* (150).clamp(0, 100); // → 100
* (-10).clamp(0, 100); // → 0
* (50).clamp(0, 100); // → 50
*/
Number.prototype.clamp ??= function (min, max) {
return Math.min(Math.max(this, min), max);
};
/**
* Formate un nombre avec séparateurs et décimales
* toLocaleString mais avec des defaults sensés.
*
* @function Number#format
* @param {number} [decimals=2] - Nombre de décimales
* @param {string} [locale='fr-FR'] - Locale pour le format
* @returns {string} Le nombre formaté
*
* @example
* (1234.5678).format(); // → "1 234,57"
* (1234.5678).format(0); // → "1 235"
* (1234.5678).format(2, 'en-US'); // → "1,234.57"
*/
Number.prototype.format ??= function (decimals = 2, locale = 'fr-FR') {
return this.toLocaleString(locale, {
minimumFractionDigits: decimals,
maximumFractionDigits: decimals
});
};
// =============================================================================
// OBJECT STATIC METHODS
// =============================================================================
/**
* Sélectionne certaines clés d'un objet
* Le destructuring mais dynamique.
*
* @function Object.pick
* @param {Object} obj - L'objet source
* @param {...string} keys - Les clés à garder
* @returns {Object} Nouvel objet avec seulement ces clés
*
* @example
* const user = { name: 'Bob', age: 30, email: 'bob@mail.com' };
* Object.pick(user, 'name', 'email');
* // → { name: 'Bob', email: 'bob@mail.com' }
*/
Object.pick ??= function (obj, ...keys) {
return keys.reduce((acc, key) => {
if (key in obj) acc[key] = obj[key];
return acc;
}, {});
};
/**
* Supprime certaines clés d'un objet
* L'inverse de pick.
*
* @function Object.omit
* @param {Object} obj - L'objet source
* @param {...string} keys - Les clés à virer
* @returns {Object} Nouvel objet sans ces clés
*
* @example
* const user = { name: 'Bob', age: 30, password: '123456' };
* Object.omit(user, 'password');
* // → { name: 'Bob', age: 30 }
*/
Object.omit ??= function (obj, ...keys) {
return Object.keys(obj).reduce((acc, key) => {
if (!keys.includes(key)) acc[key] = obj[key];
return acc;
}, {});
};
})();
Télécharger kortic-extends-prototypes.js
Nous sommes d'accord, il y a plus de commentaires que de code. Oui, et ?
Pourquoi ce truc ?
Parce qu'écrire ça :
localStorage.removeItem('user');
localStorage.removeItem('token');
localStorage.removeItem('session');
localStorage.removeItem('preferences');
C'est 2010. Et on est plus en 2010.
Maintenant on peut faire ça :
localStorage.removeItems('user', 'token', 'session', 'preferences');
Boom. Efficacité. Et un peu de flemme…
Documentation
Storage.prototype.removeItems(...keys)
Supprime plusieurs clés du localStorage/sessionStorage en une seule ligne.
Paramètres :
...keys(string) - Les clés à supprimer
Exemples :
// Nettoie tout après déconnexion
localStorage.removeItems('user', 'token', 'lastVisit');
// Fonctionne aussi avec sessionStorage
sessionStorage.removeItems('tempData', 'cache');
Storage.prototype.setItems(object)
Définit plusieurs items d'un coup. Les valeurs null sont ignorées (parce qu'on est pas des sauvages).
Paramètres :
object(Object) - Un objet clé/valeur
Exemples :
localStorage.setItems({
user: 'TaMamie',
theme: 'dark',
lang: 'fr',
uselessStuff: null // sera ignoré
});
Element.prototype.setAttributes(object)
Balance plusieurs attributs sur un élément HTML en une fois.
Le null est ignoré. Applicable dans d'autres contextes sociétaux, mais c'est un autre sujet.
Paramètres :
object(Object) - Objet clé/valeur des attributs
Exemples :
const img = document.createElement('img');
img.setAttributes({
src: 'cat.jpg',
alt: 'Un chat mignon',
loading: 'lazy',
width: '300',
'data-category': 'animals'
});
C'est tout de même plus rapide à écrire et un script moins verbeux, c'est toujours ça de pris !
Element.prototype.removeAttributes(...attributes)
Élimination à la mitrailleuse de plusieurs attributs, d'un coup. Le Marie Kondō du HTML : "Does it spark joy ? Non ? Poubelle."
Paramètres :
...attributes(string) - Les attributs à supprimer
Exemples :
const button = document.querySelector('button');
button.removeAttributes('disabled', 'aria-hidden', 'data-old');
FAQ (Foire À Quoi ?)
C'est pas dangereux de modifier les prototypes natifs ?
Si, un peu. Mais on vit qu'une fois.
Et puis on utilise ??= donc si quelqu'un a déjà défini ces méthodes, on n'écrase rien.
Mon linter me boude
Normal, c'est un linter !
Un linter, ça se configure, ou ça s'ignore. En tant que devs, nous sommes censés savoir ce que l'on fait.
Mon collègue ne comprend pas d'où viennent ces méthodes
README inside, il suffit de lire.
S'il ne comprend toujours pas, c'est qu'il est pas encore prêt pour l'évolution.
Ça marche sur quels navigateurs ?
Tout navigateur qui supporte :
??=Nullish coalescing assignment (Affectation par coalescence nulle, in french)...spread/restoperatorforEachetObject.keys
Techniquement, on n'est pas non plus dans l'envoi de fusée sur Neptune, là. Tant mieux, ce n'est que du gaz, ça ne sert rien.
Donc genre... IE11 peut aller se faire voir. Tous les navigateurs modernes, pas de souci.
Licence DWTFYWPL
DWTFYWPL - Do What The Fuck You Want to Public License
Remerciements
- À tous les devs flemmards du monde
- À la personne qui a inventé le spread operator
- À Marie Kondō, pour l'inspiration spirituelle du
.removeAttributes()
Made with laziness by developer, for developers