peterS.: [getElementsByClassName] - code review

Beitrag lesen

gruss Felix, hallo interessierte,

Du suchst nach einer Art "getElementsByClassName()"

ich habe Dir meine Funktion herausgesucht. Hier ist sie:

//  getElementsByClassName : function (className, element) {

   var getElementsByClassName = function (className, element) {  
  
   //wird spaeter benoetigt:  
     var nodeObj, nodeClassName, regXTrim = (/^\s+|\s+$/g), regXShrink = (/\s+/g);  
  

> //element = element ? element : document; // das ist keine hinreichend genaue pruefung - besser:  

     element = ((element && ((typeof element.getElementsByTagName == "function") || (typeof element.getElementsByTagName == "object"))) ? (element) : (document.documentElement || document.getElementsByTagName("html")[0]));  
  
   //auf korrektes [className] attribut wird gar nicht geprueft - kann man z.b. so bewerkstelligen:  
   //*undefinierte* [className] ausdruecke, resultieren in einer *wildcard*-suche:  
     className = ((className) ? (String(className).replace(regXTrim, "").replace(regXShrink, " ")) : ("*"));  
  

> //var muster = new RegExp("(^|\\s)" + className + "(\\s|$)");  

   //die naechste zeile beruecksichtigt den gerade erwaehnten *wildcard* modus:  
     var muster = new RegExp(((className == "*") ? ("^.+$") : ("(?:^|\\s)" + className + "(?:$|\\s)")), "");  
  

> //var alles = element.getElementsByTagName("*");  
> //var gefunden = new Array();  
> //var i;  

   //zusammenfassen:  
     var i, len, gefunden = [], alles = element.getElementsByTagName("*");  
  

> //for (i = 0; i < alles.length; i++) {  

     for (i=0, len=alles.length; i<len; ++i) {  
  

>   //if (alles[i] && alles[i].className && alles[i].className != "") {  

     //---------------------------------------------------------^^ genauer:  
     //if (alles[i] && alles[i].className && alles[i].className !== "") { besser noch:  
       nodeObj = alles[i];  
       nodeClassName = nodeObj.className;  
     //normalisiert alle *white spaces*, um jegliche inkonsistenzen/manipulationen auszuschliessen:  
       nodeClassName = (((typeof nodeClassName != "undefined") && !((typeof nodeClassName == "object") && !nodeClassName)) ? (String(nodeClassName).replace(regXTrim, "").replace(regXShrink, " ")) : (""));  
  

>     //if (alles[i].className.match(muster)) // für Fälle wie class="xyz abc"  

       if (muster.test(nodeClassName)) {  

>     //gefunden[gefunden.length] = alles[i];  

         gefunden[gefunden.length] = nodeObj;  

>     }  
>   }  
>   
>   return gefunden;  
> };

~~~  
  
  
oder auch kuerzer unter zuhilfenahme des generischen [[Array]]-iterators [filter]:  
  
  
~~~javascript
var getElementsByClassName = (function (className, nodeObj) {  
  
  var nodeClassName, regXClassName, regXTrim = (/^\s+|\s+$/g), regXShrink = (/\s+/g);  
  
  nodeObj = ((nodeObj && ((typeof nodeObj.getElementsByTagName == "function") || (typeof nodeObj.getElementsByTagName == "object"))) ? (nodeObj) : (document.documentElement || document.getElementsByTagName("html")[0]));  
  className = ((className) ? (String(className).replace(regXTrim, "").replace(regXShrink, " ")) : ("*"));  
  
  var regXClassName = new RegExp(((className == "*") ? ("^.+$") : ("(?:^|\\s)" + className + "(?:$|\\s)")), "");  
  
  return Array.filter(nodeObj.getElementsByTagName("*"), (function (elm/*, idx, arr*/) {  
  
    nodeClassName = elm.className;  
    nodeClassName = (((typeof nodeClassName != "undefined") && !((typeof nodeClassName == "object") && !nodeClassName)) ? (String(nodeClassName).replace(regXTrim, "").replace(regXShrink, " ")) : (""));  
  
    return regXClassName.test(nodeClassName);  
  }));  
});
~~~  
  
  
aber selbst die korrigierte bzw. die iterierende loesung sucht multiple  
klassennamen immer noch in einem \*strict\*-modus - d.h.: die reihenfolge  
der einzelnen schluesselwoerter innerhalb eines solchen mutiplen namens  
wird durch den jetzigen suchausdruck streng beruecksichtigt - die durch  
die css-spezifikation erlaubten abweichungen in der reihenfolge sowie  
auslassungen sind hier ausschlusskriterien und lassen die funktion nicht  
getreu dieser spezifikation arbeiten.  
  
sowohl suchausdruck als auch zu vergleichende [className]-elementattribute  
verlangen mehr aufmerksamkeit. um den regulaeren ausdruck fuer die suche  
fit zu machen, bietet es sich an, eine subroutine zu schreiben. benoetigt  
wird eine von reduntanten eintraegen bereinigte liste, aller im multiplen  
klassennamen vorkommender schluesselwoerter. darauf laesst sich dann ein  
simpler algorithmus stricken, der den anspruchsvolleren suchausdruck generiert:  
  
  
~~~javascript
//[http://www.pseliger.de/jsExtendedApi/jsApi.Array.normalize.js] - [http://www.pseliger.de/jsExtendedApi/jsApi.Array.normalize.dev.js]  
Array.prototype.normalize = (function() {var arr=this,i=0,k=0;while(i<arr.length){k=i+1;while(k<arr.length){if(arr[i]===arr[k]){arr=arr.slice(0,k).concat(arr.slice(k+1,arr.length));--k;}++k;}++i;}for(i=0;i<arr.length;++i){this[i]=arr[i];}this.length=arr.length;});  
  
var regXSplit = (/\s+/);  
  
var getRegExpSearchTerm = (function (str) {  
  
  if (regXSplit.test(str)) {  
  
    var arr = str.split(regXSplit).sort();  
    arr.normalize();  
  
    str = arr.shift();  
  
    arr.forEach(function (elm/*, idx, arr*/) {  
  
      str += ("\\s(?:" + elm + "|(?:[^\\s]+\\s)+" + elm + ")");  
    });  
  }  
  return str;  
});  
var className = "confused happy   ashamed joyful   joyful ashamed";  
var regXClassName = new RegExp(((className == "*") ? ("^.+$") : ("(?:^|\\s)" + getRegExpSearchTerm(className) + "(?:$|\\s)")), "");  
alert(regXClassName);
~~~  
  
  
die werte aller [className]-elementattribute muessen ebenfalls  
sortiert, normalisiert und in einen stringwert zuruckgefuehrt  
werden, sobald es sich bei einem solchen attributwert um eine  
multiple klasse handelt.  
  
dannach darf endlich verglichen werden - fertig.  
  
  
obige erklaerungen in code gegossen - ich bitte um tests und rueckmeldungen:  
  
  
~~~javascript
var getElementsByClassName = (function (className, nodeObj) {  
  
  var nodeClassName, regXClassName, regXTrim = (/^\s+|\s+$/g), regXShrink = (/\s+/g), regXSplit = (/\s+/);  
  
  var getRegExpSearchTerm = (function (str) {  
  
    if (regXSplit.test(str)) {  
      var arr = str.split(regXSplit).sort();  
      arr.normalize();  
      str = arr.shift();  
      arr.forEach(function (elm/*, idx, arr*/) {  
        str += ("\\s(?:" + elm + "|(?:[^\\s]+\\s)+" + elm + ")");  
      });  
    }  
    return str;  
  });  
  
  nodeObj = ((nodeObj && ((typeof nodeObj.getElementsByTagName == "function") || (typeof nodeObj.getElementsByTagName == "object"))) ? (nodeObj) : (document.documentElement || document.getElementsByTagName("html")[0]));  
  className = ((className) ? (String(className).replace(regXTrim, "").replace(regXShrink, " ")) : ("*"));  
  
  var regXClassName = new RegExp(((className == "*") ? ("^.+$") : ("(?:^|\\s)" + getRegExpSearchTerm(className) + "(?:$|\\s)")), "");  
  
  return Array.filter(nodeObj.getElementsByTagName("*"), (function (elm/*, idx, arr*/) {  
  
    nodeClassName = elm.className;  
    nodeClassName = (((typeof nodeClassName != "undefined") && !((typeof nodeClassName == "object") && !nodeClassName)) ? (String(nodeClassName).replace(regXTrim, "").replace(regXShrink, " ")) : (""));  
  
    if (regXSplit.test(nodeClassName)) {  
      nodeClassName = nodeClassName.split(regXSplit).sort();  
      nodeClassName.normalize();  
      nodeClassName = nodeClassName.join(" ");  
    }  
    return regXClassName.test(nodeClassName);  
  }));  
});
~~~  
  
  
  
so long - peterS. - pseliger@gmx.net  
  
  

-- 
»Because objects in JavaScript are so flexible, you will want to think differently about class hierarchies.  
Deep hierarchies are inappropriate. Shallow hierarchies are efficient and expressive.« - [Douglas Crockford](http://javascript.crockford.com/)  
  
[ie:( fl:) br:> va:( ls:& fo:) rl:) n3;} n4:} ss:} de:µ js:} mo:? zu:\]](http://www.peter.in-berlin.de/projekte/selfcode/?code=ie%3A%28+fl%3A%29+br%3A%3E+va%3A%28+ls%3A%26+fo%3A%29+rl%3A%29+n3%3B%7D+n4%3A%7D+ss%3A%7D+de%3A%B5+js%3A%7D+mo%3A%3F+zu%3A%5D)