Avatar billede andreas13_fam Nybegynder
11. december 2010 - 12:29 Der er 15 kommentarer og
1 løsning

SVÆR: kør kode når et element er tilføjet til dokumentet

Hej
Jeg har et script der modificere nogle elementer. Det fungere fint så længe dokumenterne er i DOM-træet fra starten af.

Problemet opstår når der oprettet et element med createElement.
I modificering af elementet indgår nemlig nogle gange ændring width og der funktionen getComputedStyle skal derfor bruges.
Men getComputedStyle kan kun give et brugbart resultat når elementet er tilføjet til dokumentet (ellers kan browseren jo ikke vide hvilke css selectors der gælder).

Jeg har derfor forsøgt mig med at udvide appendChild og insertBefore metoden. Men det virker ikke i FireFox.

Jeg søger derfor også kun en løsning som virker i firefox.

En mulighed er at lave en while-lykke der udvider appendChild på alle elementer uden om prototypen, men det kræver mange resurser.

Man kunne også stille spørgsmålet på en anden måde, "findes der et event som køre når et element bliver tilføjet til dom-træet" eller kan man lave et.

Følgende er den kode som udvider appendChild på prototype niveauet.

<!DOCTYPE html>
<html lang="da">
    <head>
        <title>AppendChild extend testcase</title>
       
        <meta charset="utf-8">
           
    </head>
    <body style="background: red;">
        <div id="test"></div>
        <script type="text/javascript">
            Element.prototype._appendChild = Element.prototype.appendChild;
           
           
            Element.prototype.appendChild = HTMLDocument.prototype.appendChild = function () {
                document.body.style.background = "green";
                return this._appendChild.apply(this,arguments);
            };
           
            document.getElementById('test').appendChild(document.createTextNode('Default AppendChild function did run'));
        </script>
    </body>
</html>
Avatar billede intenz Novice
11. december 2010 - 13:59 #1
Det er generelt ikke en god ide at udvide de eksisterende metoder, så hellere lave sin egne.

Men du kan måske udvide Object's prototype til at kunne tage en 'onready' callback funktion. Noget i den stil her:


Object.prototype.onready = function (callback) {
    callback();
    return this;
}

document.getElementById('test').appendChild( document.createTextNode('Default AppendChild function did run') ).onready(function() {
    document.body.style.background = "green";
});


Jeg ved ikke om det løser det problem du har?
Avatar billede andreas13_fam Nybegynder
11. december 2010 - 14:09 #2
Nej desvære
onready funktionen ville jo bare køre sit parameter når den bliver kaldt.

Jeg ved godt at det er dårlig maner at ændre på de native metoder, men hvis jeg skal lave et script som ikke kræver noget af brugeren andet end at tilføje scriptet i <head> så er det desvære nødvendigt.

Jeg har skrævet til mozilla for at rapportere fejlen, de skriver at de godt kender fejlen og at en måde man kan lave et workaround er ved ikke at benytte Element.prototype men alle de andre, for eksempel HTMLBodyElement.prototype.
Avatar billede intenz Novice
11. december 2010 - 14:43 #3
onready funktionen ville vel først køres når appendChild var færdig. Altså efter den er indsat i DOM (jeg går ud fra den ikke returnerer før det er gjort). Ellers kan man hacke onready ved at lave en setTimeout i der kører callback efter f.eks. 100 ms. Dog ikke den kønne løsning.
Men hvis brugeren ikke skal gøre noget (ændre deres kode), hjælper den løsning nok ikke så meget alligevel.

Jeg kan ikke se at deres workaround vil virke. Så vidt jeg kan se er det appendChild i Element prototype der bliver kørt. Så hjælper det vel ikke så meget at ændre den i en af Element's children.

Ellers har jeg ikke nogle bud :)
Avatar billede andreas13_fam Nybegynder
11. december 2010 - 15:22 #4
Ja okay. Jeg tænkte at det ikke vil virke med


var div = document.createElement('div');

    var br = document.creeateElement('br');

    div.appendChild().onready(fn);

body.appendChild(div).onready(fn);


Men jeg kan stadig ikke lide løsningen da det kræver en indsats fra udvikleren.

Med hensyn til HTMLBodyElement.prototype kunne jeg heller ikke få det til at give mening, men jeg er lige blevet færdig med et testcase som beviser at det virker:
http://pastebin.com/eyGtuUnD

Så nu skal jeg så bare lave en funktion der udnytter det.
Avatar billede andreas13_fam Nybegynder
11. december 2010 - 17:11 #5
Så blev jeg også skør i dag. Men det lykkedes til sidst med følgende funktion:


function extendElementPrototypeFunction(name, func) {
   
    //Check if it is possible to extend the Element prototype
    Element.prototype['_'+name] = Element.prototype[name];
    Element.prototype[name] = function () {return 'unique'};
   
    var simpelExtend = false;
    try {
        if (document.createElement('span')[name]() == 'unique') {
            simpelExtend = true;
        }
    } catch(err) {}
   
    //Roll back
    Element.prototype[name] = Element.prototype['_'+name];
    delete Element.prototype['_'+name];
   
    //Extend the DocumentFragment prototype, with is need anyway
    DocumentFragment.prototype['_'+name] = DocumentFragment.prototype[name];
    DocumentFragment.prototype[name] = func;
   
    //If it is possible to extend the Element prototype
    if (simpelExtend) {
        Element.prototype['_'+name] = Element.prototype[name];
        Element.prototype[name] = func;
    }
   
    //Else extend every singel Element prototype one by one
    else {
   
        //All elements except object and embed
        var elements = "unkownElement a abbr address area article aside audio b base bdi bdo blockquote body br button caption cite code col colgroup command datalist dd del details device dfn div dl dt em fieldset figcaption figure footer form h1 h2 h3 h4 h5 h6 head header hgroup hr html i iframe img input ins kbd keygen label legend li link map mark menu meta meter nav noscript ol optgroup option output p param pre progress q rp rt ruby s samp script section select small source span strong style sub summary sup table tbody td textarea tfoot th thead time title tr track ul var video wbr".split(" ");
       
        //Variabel to make sure the same Element prototype won't be extended twice
        var dubbelControl = [];
       
        //Go through all elements
        for (var i=0;i<elements.length;i++) {
           
            //Find the constructor object
            var constructorObject = document.createElement(elements[i]).constructor;
           
            //Make sure the constructor pototype hasn't been extended before
            var repeat = false;
            for (var c=0;c<dubbelControl.length;c++) {
               
                if (constructorObject == dubbelControl[c]) {
                    repeat = true;
                    break;
                }
               
            }
           
            //If the constructor pototype has been extended before, go to next element
            if (repeat) {
                continue;
            }
            //If not when add the constructor to the dubbelControl array
            dubbelControl.push(constructorObject);
           
            //And extend the prototype
            constructorObject.prototype['_'+name] = constructorObject.prototype[name];
            constructorObject.prototype[name] = func;
        }
    }
}


Eksempel til brug:

[pre]

extendElementPrototypeFunction('appendChild',function () {
    if (arguments[0].nodeType == 1) {
        arguments[0].setAttribute('test','true');
    }
   
    return this._appendChild.apply(this,arguments);
});
/pre]


Funktionen har også været igenmem testcasen og understøtter også createDocumentFragment(). Dog ikke <object> og <embed> da der var problemer med dem, deres prototype er et Object som i {} og intet andet.

Funktionen tester desuden om Element.prototype er en mulighed og det tager med mine test kun 3 ms længere tid.

Hele testcasen kan ses her, og jeg syntes selv den er smuk :)
http://pastebin.com/7xC5Gg17
Avatar billede intenz Novice
11. december 2010 - 17:41 #6
Den ser fin ud i firefox, men jeg får 2 fejl i IE8.

82: Message: 'DocumentFragment' is undefined
148: Message: Invalid procedure call or argumentLine
Avatar billede andreas13_fam Nybegynder
11. december 2010 - 19:57 #7
Tak du sage det, jeg arbejder normalt på en Mac så jeg havde helt glemt IE jeg vil se på det.
Avatar billede andreas13_fam Nybegynder
12. december 2010 - 18:42 #8
Jeg er kommet en del videre men er stødt på en irriterende fejl i IE, måske du kan hjælp:


<!DOCTYPE html>
<html lang="da">
    <head>
        <title>Testcase for unkown prop</title>
        <meta charset="utf-8">
    </head>
    <body>
        <pre></pre>
        <script type="text/javascript">
       
            var test = function (str) {
                document.body.getElementsByTagName('pre')[0].appendChild(document.createTextNode(str.toString() + "\n"));
            };
           
            var constructorObject = document.createElement('area').constructor;
           
              constructorObject.prototype['_appendChild'] = constructorObject.prototype['appendChild'];
            constructorObject.prototype['appendChild'] = function () {
               
                test(typeof this['_appendChild'].apply);
               
                this['_appendChild'].apply(this,arguments);
            };
           
            document.createElement('area').appendChild(document.createElement('br'));
       
        </script>
    </body>
</html>
Avatar billede andreas13_fam Nybegynder
12. december 2010 - 20:55 #9
Så nu virker det også i IE:
http://pastebin.com/zqbh51mB

Der er enkelte røde men der er bare fordi IE ikke få det til at give mening. At tilføje et br element til et br element giver fx ikke mening.

Hvis du kan finde flere fejl må du meget gerne skrive.
Avatar billede andreas13_fam Nybegynder
12. december 2010 - 21:12 #10
Okay det virker ikke i IE7 og nedaf. Men det gør ikke så meget.

Testcase der ikke fejler i IE7 og nedaf:
http://pastebin.com/qxS9cxr8
Avatar billede intenz Novice
12. december 2010 - 22:11 #11
Det ser ud til at virke her :)

Af ren nysgerrighed, hvad skal du bruge det til?
Avatar billede andreas13_fam Nybegynder
12. december 2010 - 22:51 #12
Hej intenz

Jeg har længe haft forskellige scripts der har hjulpet med at understøtte de nye HTML5 Forms standarder, (før i tiden web form 2). Men forsøgte så på et tidspunkt at samle dem til et script hvilket også fungerede, men når man lavede et element dynamisk eller ændrede noget dynamisk fungerende det ikke.

Jeg har derfor nu tænkt mig at starte forfra  og lave et supper godt script der tilføjer funktionaliteten.
Jeg er ikke noget så langt men mit projekt ligger her: https://github.com/AndreasMadsen/HTML5-Forms

Skriv endelig hvis du vil være en del af det, når jeg er færdig med system kernen håber jeg at jeg kan få flere med på ideen.

Hvis det er ok med dig bliver pointfordeling:
intenz: 50
mig: 100
Avatar billede intenz Novice
13. december 2010 - 00:12 #13
Spændende, og ambitiøst :) Jeg har nu ikke tiden til at arbejde på sådan noget :)

Men jeg har 'watch'ed dig på github. Du kan bare snuppe point selv, det betyder ikke noget.
Avatar billede andreas13_fam Nybegynder
13. december 2010 - 12:45 #14
Det er bare fint
Avatar billede andreas13_fam Nybegynder
17. december 2010 - 21:06 #15
I forbindelse med at jeg skulle undersøge om ondomready eventet, støtte jeg på MutationEvent hvilket er lige hvad jeg skal bruge. Der findes for eksempel et DOMNodeInsertedIntoDocument event, navnet siger hvis alt. Alle browser understøtter det selvfølgelig ikke men det er da meget bedre end at extende appendChild og nu kan jeg jo nøjes med at extende appendChild hvis browseren ikke understøtter det.

Jeg derfor nu kun at finde en DOMNodeInsertedIntoDocument løsning for IE6-IE7.
Avatar billede andreas13_fam Nybegynder
17. december 2010 - 21:09 #16
PS: måden hvor ved DOMNodeInsertedIntoDocument  kan bruges, må være ved at extende createElement, det er selvfølgelig ikke noget der givet street credit. Men det er da langt mindre omfattende en at extende alle element prototypes der findes.
Avatar billede Ny bruger Nybegynder

Din løsning...

Tilladte BB-code-tags: [b]fed[/b] [i]kursiv[/i] [u]understreget[/u] Web- og emailadresser omdannes automatisk til links. Der sættes "nofollow" på alle links.

Loading billede Opret Preview
Kategori
Vi tilbyder markedets bedste kurser inden for webudvikling

Log ind eller opret profil

Hov!

For at kunne deltage på Computerworld Eksperten skal du være logget ind.

Det er heldigvis nemt at oprette en bruger: Det tager to minutter og du kan vælge at bruge enten e-mail, Facebook eller Google som login.

Du kan også logge ind via nedenstående tjenester