dQuery - Une version compatible IE9 / ES5
2019-05-07 #javascript#jquery
Cette série de billets retrace quelques-unes des étapes pour développer une mini-librairie JavaScript qui remplacera(it) un jour jQuery sur mon site de jeux de solitaires.
Pour l'instant, je m'accorde encore le besoin d'être compatible avec IE9, et par conséquent EcmaScript 5.
Il est certain qu'aujourd'hui, ce n'est pas franchement indispensable. Mais je me dis qu'il y a bien quelques joueurs en entreprises qui doivent se coltiner un PC avec IE9 pour faire tourner de vieilles applications intranet que personne ne veut mettre à jour...
Et puis surtout, le côté "challenge" me donne l'occasion de "mieux" étudier et maitriser le code que je fais (j'espère).
Quoiqu'il en soit, étant donné la taille de NanoJS, il n'y a franchement rien d'insurmontable à assurer un maximum de compatibilité avec le navigateur Internet Explorer 9.
Premier truc, dans NanoJS, la méthode .removeClass()
ne fonctionne que si la
propriété .classList
existe (donc pas IE9). Alors que la méthode .addClass()
gère ça et passe par la propriété .className
(connue de IE9) si besoin.
Donc une méthode .removeClass()
basée que la propriété .className
quand
nécessaire :
removeClass: function (v) {
return this.each(function (i) {
if (i.classList) {
i.classList.remove(v);
} else {
i.className = i.className.replace(" " + v, "");
}
});
},
Ou mieux, avec une expression régulière copiée / collée d'internet :
removeClass: function (v) {
return this.each(function (i) {
if (i.classList) {
i.classList.remove(v);
} else {
i.className = i.className.replace(new RegExp(v + " *", "g"), "");
}
});
},
Puis un truc facile. La propriété .innerText
n'est pas "standard" et il est
préférable d'employer .textContent
(ce n'est pas vraiment un problème lié à
IE9, mais plutôt avec les vieux Firefox) :
text: function (v) {
return this.each(function (i) {
i.textContent = v;
});
},
Pour la méthode .css(propriété, valeur)
, les propriétés snake-case posent
problème :
element.style["background-color"] = "red"
=> KOelement.style["backgroundColor"] = "red"
=> OK
Il suffit donc de transformer le libellé des propriétés snake-case en camel-case :
css: function (a, v) {
a = a.replace(/(\-\w)/g, function(m) { return m[1].toUpperCase(); });
return this.each(function (i) {
i.style[a] = v;
});
},
Pour finir, je me suis débarrassé du seul $("#win").is(":visible")
dont
j'avais besoin dans mes sources. J'ai remplacé ça par du code basé non plus sur
le résultat (est-ce que l'écran de réussite est visible) mais sur le fait que la
partie était gagnée ou pas (game.state !== 0
). Ce qui m'a permis de supprimer
la méthode .is(...)
qui était quelque peu frauduleuse :
is: function (v) {
// Toujours $(..).is(":visible")
return this.value[0].offsetParent !== null;
},
Ce n'était vraiment pas compliqué, et au final ma version source "arrangée" de NanoJS est la suivante :
!(function () {
var nano = function (s) {
if (typeof s === "string") {
this.value = Array.prototype.slice.call(document.querySelectorAll(s));
} else if (typeof s === "object") {
this.value = [s];
}
};
nano.prototype = {
each: function (fn) {
[].forEach.call(this.value, fn);
return this;
},
css: function (a, v) {
a = a.replace(/(\-\w)/g, function(m) { return m[1].toUpperCase(); });
return this.each(function (i) {
i.style[a] = v;
});
},
hide: function () {
return this.each(function (i) {
i.style.display = "none";
});
},
show: function () {
return this.each(function (i) {
i.style.display = "block";
});
},
on: function (type, fn) {
return this.each(function (i) {
i.addEventListener(type, fn, false);
});
},
addClass: function (v) {
return this.each(function (i) {
if (i.classList) {
i.classList.add(v)
} else {
i.className += " " + v;
}
});
},
removeClass: function (v) {
return this.each(function (i) {
if (i.classList) {
i.classList.remove(v);
} else {
i.className = i.className.replace(new RegExp(v + " *", "g"), "");
}
});
},
html: function (v) {
return this.each(function (i) {
i.innerHTML = v;
});
},
append: function (v) {
return this.each(function (i) {
i.insertAdjacentHTML("beforeend", v);
});
},
text: function (v) {
return this.each(function (i) {
i.textContent = v;
});
},
remove: function () {
return this.each(function (i) {
i.parentNode.removeChild(i);
});
},
parent: function () {
this.value = [this.value[0].parentNode];
return this;
},
attr: function (v) {
return this.value[0].getAttribute(v);
},
};
window.$ = function (selector) {
return new nano(selector);
};
})();
Et toujours moins de 100 lignes de code !