Source: « Variable » variables in JavaScript
1 2 3 |
var data = "testVariable"; window["temp_" + data] = 123; alert(window["temp_" + data]); |
Le bloc-notes professionnel d'un développeur front-end senior
Source: « Variable » variables in JavaScript
1 2 3 |
var data = "testVariable"; window["temp_" + data] = 123; alert(window["temp_" + data]); |
Objectif: boucler dans l’objet JSON ci-dessous et retourner une liste de toutes les valeurs de la clé example
.
Ressources en ligne:
index
des items de l’objet JSON pour cibler le premier, le dernier, etc… item du tableau: jQuery .each get index of items
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
{ "pattern_1": { "example": "AB12 3CD", "pattern": "^[a-zA-Z]{2}[0-9]{2}\\s?[0-9]{1}[a-zA-Z]{2}$" }, "pattern_2": { "example": "A1B 2CD", "pattern": "^[a-zA-Z]{1}[0-9]{1}[a-zA-Z]{1}\\s?[0-9]{1}[a-zA-Z]{2}$" }, "pattern_3": { "example": "AB1 2CD", "pattern": "^[a-zA-Z]{2}[0-9]{1}\\s?[0-9]{1}[a-zA-Z]{2}$" }, "pattern_4": { "example": "AB1C 2DF", "pattern": "^[a-zA-Z]{2}[0-9]{1}[a-zA-Z]{1}\\s?[0-9]{1}[a-zA-Z]{2}$" }, "pattern_5": { "example": "A12 3BC", "pattern": "^[a-zA-Z]{1}[0-9]{2}\\s?[0-9]{1}[a-zA-Z]{2}$" }, "pattern_6": { "example": "A1 2BC", "pattern": "^[a-zA-Z]{1}[0-9]{1}\\s?[0-9]{1}[a-zA-Z]{2}$" } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
function processList(data) { var examples = ''; var i = 0; $.each(data, function(index, value) { var length = Object.keys(data).length; if (i === (length -1)) { examples += value.example; } else { examples += value.example + '; '; } i++; }); return examples; } // Result: // AB12 3CD; A1B 2CD; AB1 2CD; AB1C 2DF; A12 3BC; A1 2BC |
The main difference between throttling and debouncing is that throttling executes the function at a regular interval, while debouncing executes the function only after some cooling period.
Source: Debouncing and Throttling in JavaScript
Version PDF: Debouncing and Throttling in JavaScript
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
if ($('[data-code="buckle_material"]', '#product-options-wrapper').length) { // Prevent layout shift for products with buckle_material as a swatch (especially when out of stock) var timerId; function showInfoMain() { infoMain.removeClass('hidden'); } function debounce(func, delay) { clearTimeout(timerId); timerId = setTimeout(func, delay); } infoMain.on('change', function() { debounce(showInfoMain, 200); }); } else { infoMain.removeClass('hidden'); } |
…et en fait j’ai jamais réussi à la faire fonctionner.
Source: How to respond to a Javascript event only if it is fired once and then not fired again during some time period?, Debouncing Javascript Methods
In my application I listen to the Google Maps API ‘bounds_changed’ event to send an ajax request to update some div on the web page depending on the new boundaries of the map:
1 2 3 |
google.maps.event.addListener(map, 'idle', function() { // here goes an ajax call } |
The event ‘idle’ is fired with a high frequency when the user drag the map around. So much that there are too many ajax requests sent to the server.
Basically I would like to make the function to be executed only after the user has stopped to move the map during some time period (for example 500ms).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
function debounce(func, threshold, execAsap) { var timeout; return function debounced () { var obj = this, args = arguments; function delayed () { if (!execAsap) func.apply(obj, args); timeout = null; }; if (timeout) clearTimeout(timeout); else if (execAsap) func.apply(obj, args); timeout = setTimeout(delayed, threshold || 100); }; } |
A l’addListener pour l’événement idle de l’API Google Maps, on déclare une fonction qui contiendra juste notre appel à la fonction debounce:
1 |
google.maps.event.addListener(mapApi.mapObj, 'idle', topic_onIdleDebounce); |
1 2 3 4 5 |
function topic_onIdleDebounce() { console.log('%c [topic_onIdleDebounce]', 'background: tomato'); debounce(topic_onIdle(), 300, true); } |
1 2 3 4 5 |
function topic_onIdle() { console.log('%c [topic_onIdle]', 'background: tomato'); // Tout le code à exécuter une fois que l'événement idle s'est déclenché pour la dernière fois dans les limites du timeout (300) défini dans notre fonction topic_onIdleDebounce } |
Source: un billet clair, concis et efficace: How to use Geolocation API with Promises
1 2 3 4 5 |
function getPosition(options) { return new Promise(function(resolve, reject) { navigator.geolocation.getCurrentPosition(resolve, reject, options) }); } |
1 2 3 4 5 6 7 |
getPosition(options) .then((position) => { console.log(position); }) .catch((err) => { console.error(err.message); }); |
async
, await
:
1 2 3 4 5 6 |
try { const position = await getPosition(options); console.log(position); } catch (err) { console.error(err.message); } |
(comprendre: sans que l’utilisateur n’ait cliqué au préalable).
J’ai rencontré ce souci en cherchant à ajouter un addEventListener
'click'
à des markers que je créais dans ma fonction pour les placer sur une Google Maps.
Avec la déclaration suivante, la fonction invoquée ci-dessous en clickCallback
était exécutée immédiatement:
1 |
google.maps.event.addListener(marker, 'click', clickCallback(marker)); |
Pour pallier à mon problème, j’ai dû déclarer mon événement comme suit:
1 2 3 |
google.maps.event.addListener(marker, 'click', function(){ clickCallback(marker); }); |
La fonction dans laquelle j’initialise mon eventListener:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
function setMarker(point, m, clickCallback) { // [PNG version]] let marker = new google.maps.Marker({ position: point.position, icon: mapOperator.icons[point.type].icon }); // Marker events google.maps.event.addListener(marker, 'click', function(){ clickCallback(marker); }); // END: Marker events return marker; } |
L’exécution de cette fonction:
1 |
mapApi.setMarker(point, i, mapOperator.topic_onMarkerClick); |
La callback function:
1 2 3 |
function topic_onMarkerClick(marker) { console.log("click!"); } |
Problème: des événements de l’API Google Maps v3 comme bounds_changed, zoom_changed, idle
se déclenchent à chaque exécution de la méthode fitBounds
qui permet d’ajuster automatiquement le zoom de la carte afin que celle-ci affiche l’ensemble des markers de la map.
MAIS… comme très souvent remonté (ici et là par exemple), l’exécution de la méthode fitBounds
, incontournable si on souhaite afficher tous les markers disponibles sur la carte dans le viewport, va très logiquement déclencher les événements code>bounds_changed, zoom_changed, idle, puisqu’elle va faire « bouger » la carte lorsqu’elle va zoomer ou dézoomer. Si votre fonction fitBounds
est exécutée au délenchement d’un des événements pré-cités, vous entrez dans une boucle infinie: l’exécution de fitBounds recommence encore er encore jusqu’à faire crasher le navigateur.
Solution: faire un mix de ce qui est suggéré ici: Do not fire ‘zoom_changed’ event when calling fitBounds function on map et ici: Fire javascript function 2 sec after no activity in input tag.
Créer un flag qui n’autorise pas les événements de la map à être exécutés si il est assigné (comme par défaut) à false
.
1 |
let allowMapEventsToBeFired = false; |
Mettre en place un premier timer
avec un seuil de 2 secondes qui se remettra à zéro et recommencera à chronométrer à chaque fois que l’event sera déclenché. Tant que ce timer n’aura pas atteint 2 secondes, il empêchera l’événement (bounds_changed
dans notre exemple) de se déclencher réellement parce que le flag restera à false
. Ce qu’on cherche ici, c’est d’éviter qu’en cas de boucle, le code assigné au déclenchement de l’événement ne soit exécuté encore et encore et encore…
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
let timer; let timer2; function onInput() { clearTimeout(timer); clearTimeout(timer2); timer = setTimeout(function(){ mapOperator.allowMapEventsToBeFired = true }, 1999); if ( mapOperator.allowMapEventsToBeFired === false ) { console.log('mapOperator.allowMapEventsToBeFired: ', mapOperator.allowMapEventsToBeFired); return false; } else if ( mapOperator.allowMapEventsToBeFired ) { console.log('mapOperator.allowMapEventsToBeFired: ', mapOperator.allowMapEventsToBeFired); timer2 = setTimeout(mapOperator.topic_updatesOnBoundsChanged.bind(this), 1000); } } google.maps.event.addListener(mapApi.mapObj, 'bounds_changed', onInput, false); |
1 2 3 4 5 6 7 8 9 |
function topic_updatesOnBoundsChanged() { // [TODO] vérifier si il y a de nouveau markers à afficher? /* On affiche/masque le bouton refreshMap */ utils.toggleElementVisibility(refreshMap.$refreshMapButton); /* au click sur le bouton refreshMap: relancer le bordel */ refreshMap.$refreshMapButton.addEventListener('click', mapOperator.topic_updatesOnMapEvents); } |
Source: How to use map function and arrow function inside another function?
1 2 3 4 |
function arrayCalc(arr, fn){ const arrRes = arr.map(a => fn(a)); // and not .map(arr => arr) return arrRes; // missing return statement } |
Ou:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
function setMarkers(sourceArray, handleBounds = true) { // Autozoom and autocenter for Google maps Clustermap // https://stackoverflow.com/a/40109734 let labels = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'; if (handleBounds) { mapApi.boundsObj = mapApi.setBoundsRestriction(); } const arrRes = sourceArray.map(function(location, i) { // console.log('location: ', location); if (handleBounds) { mapApi.boundsObj.extend(location); /* Sets the viewport to contain the given bounds. */ mapApi.mapObj.fitBounds(mapApi.boundsObj); } let marker = new google.maps.Marker({ position: location, label: labels[i % labels.length] }); return marker; }); return arrRes; } |
1 |
stateSynchronizer.allMarkersObj = mapApi.setMarkers(stateSynchronizer.allLocationsArr, false); |
Plusieurs sources pour cet article (car plusieurs techniques; à vous de choisir):
window['varName'] = varName;
: How to avoid global variables in JavaScript?Je détaille celui-ci car il me plait bien 🙂 Mais voir également le Revealing module pattern juste en-dessous qui apporte quelques améliorations.
return
return
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
YAHOO.myProject.myModule = function () { //"private" variables: var myPrivateVar = "I can be accessed only from within YAHOO.myProject.myModule."; //"private" method: var myPrivateMethod = function () { YAHOO.log("I can be accessed only from within YAHOO.myProject.myModule"); } return { myPublicProperty: "I'm accessible as YAHOO.myProject.myModule.myPublicProperty." myPublicMethod: function () { YAHOO.log("I'm accessible as YAHOO.myProject.myModule.myPublicMethod."); //Within myProject, I can access "private" vars and methods: YAHOO.log(myPrivateVar); YAHOO.log(myPrivateMethod()); //The native scope of myPublicMethod is myProject; we can //access public members using "this": YAHOO.log(this.myPublicProperty); } }; }(); // the parens here cause the anonymous function to execute and return |
Avec le Revealing module pattern, toutes les variables et toutes les fonctions sont d’abord déclarées en privé et on se sert du return
pour lister celles qu’on rend ensuite publiques. C’est plus lisible et plus pratique à l’utilisation.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
var myModule = (function() { 'use strict'; var _privateProperty = 'Hello World'; var publicProperty = 'I am a public property'; function _privateMethod() { console.log(_privateProperty); } function publicMethod() { _privateMethod(); } return { publicMethod: publicMethod, publicProperty: publicProperty }; })(); myModule.publicMethod(); // outputs 'Hello World' console.log(myModule.publicProperty); // outputs 'I am a public property' console.log(myModule._privateProperty); // is undefined protected by the module closure myModule._privateMethod(); // is TypeError protected by the module closure |