RIDITLETEUR-A virtual DOM in 200 lines of JavaScript – Marcelo Lazaroni – Developing for the Interwebs
Comprendre le virtualDom en en créant un de toutes pièces ? ça a l'air intéressant...
Comprendre le virtualDom en en créant un de toutes pièces ? ça a l'air intéressant...
Donc, pour résumer:
console.log({var})
console.warn()
, console.error()
et console.info()
pour différencier l'aspect du message.console.assert(condition,retour)
pour éviter un if (condition){console.log(retour)
console.trace()
pour remonter la pile d'appelsconsole.time('etiquette')
et console.timeEnd('etiquette')
pour mesurer un temps d'exécution entre deux pointsconsole.group('nom')
, console.groupCollapsed('nom')
et console.groupEnd('nom')
pour regrouper des console log() console.table(array)
pour présenter les données sous forme de tableauconsole.dir(array)
pour présenter hiérarchiquement un tableau $(selecteur)
est équivalent à document.querySelector(selecteur)
$$(selecteur)
est équivalent à document.querySelectorAll(selecteur)
Liste complétée avec https://dev.to/alishgiri/say-no-to-consolelog-556n
En bossant sur l'appli que j'utilise pour mes cours, j'ai voulu ajouter un accès rapide à un document en tapant le début du nom de dossier dans un input alimenté par une datalist
.
Bien entendu, je veux éviter de devoir passer du clavier à la souris plusieurs fois : tape un bout du nom, clique pour sélectionner, appuie sur entrée pour valider...
Donc, il faudrait que la sélection et l'ouverture du document se fasse:
En effet, autant on peut capturer un évènement sur un input, autant ce n'est pas possible sur les option
ou le datalist
Donc, il faut gruger et agissant sur les events de l'input.
Je mets ici ma solution actuelle (c'est le code que j'utilise avec mon framework js perso parce que j'ai la flemme de changer ):
<input type="text" placeholder="accès rapide à un document" list="docs_list" id="input_docs_list">
<datalist id="docs_list">
\\ici les option qui vont bien
</datalist>
<script type="text/javascript">
on('keydown','#input_docs_list',function(e){ // ça, c'est mon VanillaJS
if(e.keyCode==13||!e.key){
// si on a appuyé sur entrée ou que l'évènement n'est pas déclenché par une touche (donc, c'est la souris)
window.location.replace("?"+e.target.value);
}
});
on('input','#input_docs_list',function(e){
// celui-ci est pour firefox
if(e.inputType=='insertReplacementText'){
window.location.replace("?"+e.data);
}
})
</script>
J'ai testé sous Firefox, Chromium et Edge...
Je me note ici pour une prochaine fois parce que fetch n'est pas forcément très intuitif...
fetch("index.php", { method: 'POST', body: formData })
.then((response)=>{
// on attend l'arrivée de la réponse et on la traite
return response.text(); // ou response.json();
})
.then((text)=>{
// on attend la fin du traitement de la réponse et on en traite le contenu
console.log(text);
});
En gros, on crée une fonction asynchrone pour pouvoir utiliser les await.
const fetchAPI = async(URL) => {
const response = await fetch(URL); // on attend l'arrivée de la réponse
const data = await response.json(); // on attend la fin du traitement de la réponse
console.log(data)
}
fetchAPI("https://jsonplaceholder.typicode.com/todos/1")
Ménon... Mais dites-moi que c'est pas vrai...
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg xmlns="http://www.w3.org/2000/svg" width="500" height="500">
<circle cx="250" cy="250" r="210" fill="#fff" stroke="#000" stroke-width="8"/>
<script type="text/javascript">alert(1);</script>
</svg>
Donc, si on colle du JS dans un SVG, le JS serait exécuté à l'affichage de l'image ?!
Je teste ci-dessous avec l'exemple donné sur la page...
Sur mon site, l'image s'affiche normalement sans exécuter le code... par contre, si on ouvre l'image dans un autre onglet...
Comment c'est possible de laisser un truc pareil dans la nature ?!
scrollIntoView()
➜ https://www.alsacreations.com/astuce/lire/1883-Le-scroll-maitrise-avec-scrollIntoView.htmlUn petit résumé perso de cette page sur l'objet FormData en javascript.
let form = document.querySelector('form');
let data = new FormData(form);
['clé','valeur']
for (let entry of data) {
console.log(entry);
}
ou
for (let [key, value] of data) {
console.log(key);
console.log(value);
}
for (let key of data.keys()) {
console.log(key);
}
let title = data.get('title')
let titles = data.getAll('title');
data.set('date', '2022-12-25');
data.append('tags', 'vacations');
let hasID = data.has('id');
let arr = Array.from(data.keys());
let arr = Array.from(data.values());
let serialized = Object.fromEntries(data);
let obj = {};
for (let [key, value] of data) {
if (obj[key] !== undefined) {
if (!Array.isArray(obj[key])) {
obj[key] = [obj[key]];
}
obj[key].push(value);
} else {
obj[key] = value;
}
}
let stringified = JSON.stringify(obj);
Pour écouter un click hors d'un élément, on utilise l'event delegation: on vérifie si la target de l'event correspond ou pas à l'objet qu'on veut.
document.addEventListener('click', e => {
if (!element.contains(e.target)) callback();
});
};
onClickOutside('#my-element', () => console.log('Hello'));
// Will log 'Hello' whenever the user clicks outside of #my-element
Tiens, je ne savais pas qu'addEventListener acceptait des paramètres en option, en particulier un { once : true} qui permet de n'exécuter la fonction attachée à l'évènement qu'une seule fois...
const listenOnce = (el, evt, fn) =>
el.addEventListener(evt, fn, { once: true });
listenOnce(
document.getElementById('my-btn'),
'click',
() => console.log('Hello!')
); // 'Hello!' will only be logged on the first click
Un autre petit plugin vite-fait-sur-le-gaz comme aurait dit San Antonio: cette fois-ci, il s'agit de styler les commentaires et articles offline pour les repérer plus simplement.
Le plugin se contente d'ajouter une classe de votre choix (à configurer) dans la ligne de tout commentaire ou article hors ligne.
Comme pour le précédent, il injecte un JS qui ajoute la classe au tr qui contient une liste offline.
Une fois activé, configurez le plugin en lui donnant la classe à utiliser ( par exemple «offline» ) puis ajoutez votre css à votre thème.
.offline{
background:rgba(255,0,0,0.2);
color:rgb(50,0,0);
font-style: italic;
}
.offline td{
padding:1em 0;
}
C'est tout. Mais visuellement, c'est autre chose !
J'ai ajouté la possibilité de déplacer tout item offline en début de liste quelle que soit sa date... Utile si vous avez tendance à accumuler le boulot en retard au point que certains disparaissent dans la pagination...
Pour récupérer le zip ➜ https://repo.warriordudimanche.net/ZIPFILES/offlineClass.zip
Si tu publies des bouts de code sur pluXML, tu as sans doute remarqué que ce dernier est pour le moins psychorigide avec les antislashes: il les vire sans ménagement... pourtant, on aimerait bien qu'il conserve les regex en l'état dans un code par exemple.
Je suis pas stupide, je comprends bien qu'il s'agit là d'un souci de sécurité, mais quand même, ça fait ch***.
Le souci,c'est que pluXML filtre les antislashes dans les $_POST
très très tôt dans le code, bien avant tout hook de plugin: il le fait dans le prepend.php de l'admin, ligne 43.
Ben mon con, comment tu vas faire si tu peux pas intervenir en php via un hook pour modifier le $_POST
?
Ben tu le modifies en JS dans la page article lors du submit du formulaire
En gros, on remplace les «\» par des «\» dans le chapo et le avant de submit le formulaire. C'est une méthode dont j'ai trouvé l'idée ici https://forum.pluxml.org/discussion/6960/plugin-plx-cssbackslash-pour-ledition-des-fichiers-css
Donc, j'injecte un petit script JS via le hook
(function (){
let chapo = document.getElementById("id_chapo");
var content = document.getElementById("id_content");
var form = document.getElementById("form_article");
function saveSlashes(str){
return str.replace(/[\\]/g, '\\$&');
}
form.addEventListener('formdata',function(e){
let formData = e.formData;
formData.set('chapo', saveSlashes(formData.get('chapo')));
formData.set('content', saveSlashes(formData.get('content')));
});
})();
Niveau sécurité, le plugin n'intervient que sur la page article.php et uniquement sur les champs chapo et content du formulaire. Il ne bloque pas le plxUtils::unSlash($_POST)
du prepend.php. Je pense donc qu'il n'ouvre pas de faille majeure.
Ainsi, les antislashes de ce script tiré de l'article sur le bug de mpdération passent enfin sans problème :
const PATTERNS = array(
'arts' => '#^\D?(\d{4,})\.(?:\w+|\d{3})(?:,\w+|,\d{3})*\.\d{3}\.\d{12}\..*\.xml$#',
'statiques' => '#^(\d{3,})\..*\.php$#',
'commentaires' => '#^_?\d{4,}\.(?:\d{10,})(?:-\d+)?\.xml$#'
);
Le zip est à télécharger depuis mon repo : https://repo.warriordudimanche.net/ZIPFILES/ArticleBackslashesKeeper.zip
https://antoineboursin.fr/courses/creez-un-editeur-de-texte-wysiwyg
La vie est mal faite: je découvre document.execCommand() permettant de faire du richtext dans un élément contentEditable juste quand il est officiellement déclaré obsolète...
Bon, en même temps, sur stackoverflow, the holy baïbol, certains affirment que:
Petite note sur un usage du dataset en js (l'attribut «data-quelquechose») Ici il utilise un attribut «data-json» pour stocker, modifier des données et transformer le formulaire dynamiquement.
Petit bout de code fait à la va-vite dans le but de vérifier la validité des liens d'une page. Mon objectif est de pouvoir à terme montrer automatiquement les liens ne répondant plus sur la page où je regroupe les liens de téléchargement illégal
Dans le code ci-dessous, j'utilise fetch et les promesses pour lancer des requêtes afin de styler les liens testés:
function checkLinks(nodelist){
function checkUrl(link){
return fetch(link).then(function(response){
return response.status;
}).catch(function(error){
return error.status;
});
}
if (!nodelist){
checkLinks(document.querySelectorAll('.checkLink'));
return;
}
for (let obj of nodelist){
if (obj.tagName=="A"){
checkUrl(obj.href).then(function(response){obj.classList.add("status"+response);});
}else if (obj.hasAttribute("src")){
checkUrl(obj.src).then(function(response){obj.classList.add("status"+response);});
}else{
checkLinks(obj.querySelectorAll("*[href],*[src]"));
}
}
}
checkLinks();
J'ai d'abord fait une fonction qui appelle une URL et renvoie une promesse qui, une fois résolue, renverra le statut de la requête. On y retrouve .then
qui renvoie le statut d'une requête qui aboutit et .catch
qui renvoie celui d'une requête se soldant par une erreur.
function checkUrl(link){
return fetch(link).then(function(response){
return response.status;
}).catch(function(error){
return error.status;
});
}
Les plus coquinous d'entre-vous me feront remarquer à juste titre que, vu ce que je renvoie, je pouvais me contenter de
function checkUrl(link){
return fetch(link).then(function(response){
return response.status;
});
}
Ceci dit, en prévoyant les deux cas, je m'autorise à gérer le retour différemment selon si ça aboutit ou pas (par exemple retourner «ok200» ou «error404»
Ce block n'est là que pour simplifier la vie de l'utilisateur en lui évitant de procéder lui-même au querySelectorAll()
if (!nodelist){
checkLinks(document.querySelectorAll('.checkLink'));
return;
}
Je parcours la nodeList en vérifiant l'URL passée en href (pour les A) ou en src (pour les img par exemple).
Le dernier cas est celui où l'on souhaite vérifier les liens contenus dans un div portant la classe .checkLink
: il suffit d'appeler la même fonction de façon récursive en lui fournissant le nodeList des liens contenus das le DIV en question. (ça permet de vérifier un grand nombre de liens sans avoir à leur ajouter individuellement la classe .checkLink
, ce qui est particulièrement utile quand on publie des articles en utilisant markdown )
for (let obj of nodelist){
if (obj.tagName=="A"){
checkUrl(obj.href).then(function(response){obj.classList.add("status"+response);});
}else if (obj.hasAttribute("src")){
checkUrl(obj.src).then(function(response){obj.classList.add("status"+response);});
}else{
checkLinks(obj.querySelectorAll("a,img"));
}
}
Il suffit de mettre la classe .checkLink
à tout objet dont on veut tester les liens et de coller la fonction dans la page puis de l'appeler via un checkLinks();
de bon aloi.
En l'état, la fonction ajoutera une classe .status200
pour les liens ok ou .status404
pour les URL qui ne répondent plus.
Il ne reste plus qu'à styler ces classes en changeant la couleur, le fond ou en ajoutant des emoji avec un petit content
. On peut même éventuellement masquer un objet dont l'URL ne répond pas...
Le script étant en JS, il se heurte évidemment aux règles de la politique CORS: toute requête hors du domaine en cours va échouer à moins de redéfinir le CORS dans le Head de la page via
<meta http-equiv="Content-Security-Policy" content="Content-Security-Policy:..."/>
Comme d'habitude, il s'agit autant d'un proof of concept que d'une truc utile... en tout cas, n'hésitez pas à en faire rigoureusement ce que vous voulez: c'est cadeau...
L'élément template
pour créer des objets à cloner via javascript de façon plus sémantique que via des div
cachées.
template
est inerte: les styles ne s'appliquent pas, les images ne se chargent pasdiv
peut échouer, être désactive ou redéfini ailleurs alors que template
est toujours cachédiv
cachées, pas les template
(à priori)