Unitadtechnologystandards / HTML5Lib

HTML5Lib - a collection of scripts to use within HTML5 AdCreation and AdServing
6 stars 3 forks source link

Trotz guter Anleitung klappt es nicht. #5

Open leolo69 opened 4 years ago

leolo69 commented 4 years ago

Hallo, ich weiß gerade nicht mehr wirklich weiter. Habe versucht, die Scripte in zwei Iframes einzubauen und diese miteinander kommunizieren zu lassen. Zum Kommunikationspart komme ich aber leider erst gar nicht, weil er schon bei der Abfrage (if (frameAccessElem && currentFrame[this.name])) nicht durchkommt, heißt, daß "currentFrame[this.name]" als "undefined" den Prozess stoppt. Verstehe sicherlich das Konstrukt nicht komplett aber glaube, daß gerade diese Funktion und die in dieser aufgerufenen "finalize"-Funktion schon zentral sind.

Was kann ich falsch gemacht haben? Lieben Dank. Alex

smoremann commented 3 years ago

@leolo69 Wenn bei dir das Problem noch besteht ich habe da für mich gestern eine paar Änderungen erstellt.

Die Idee des Scripts ist es alle Iframes mit einem window.name : 'MyConnectionID' (oder was auch immer deine ID ist) zu finden nachdem ein ContentLoaded Event im Iframe gefeuert wurde.

Das heißt jedes Iframe sucht jedes andere IFrame mit der MyConnectionID und wenn es windowCount Anzahl von IFrames gibt ist das aktuelle IFrame das letzte geladene Iframe und triggert dann an alle anderen IFrameWindows ('MyConnectionID') das Event("frameconnectorstart") und das ruft dann in jedem IFrame die Funktion localConnectStart auf.

Ich hoffe du hast das Problem nicht mehr, oder das hilft :) Alles was ich in "bold" abgebildet habe habe ich angepasst bzw. hinzugefügt. Es gibt bessere Wege die Objekte zu halten und zu nutzen, wie immer ich musst nur fertig werden ;)

var _loadedCounter = 0; 'use strict'; function Connector(conf) { window.name = this.name = conf.connectionName; window.windowName = this.windowName = conf.windowName; window.windowCount = this.windowCount = conf.windowCount; this.windows = {}; this.windowSearch = {}; } Connector.prototype.finalize = function () { Object.keys(this.windowSearch).forEach(function (wn) {

this.windowSearch[wn].windows = this.windowSearch;
// IE & Edge fix for dispatching the event var event; if(typeof(Event) === 'function') { event = new Event('frameconnectorstart'); }else{ event = document.createEvent('Event'); event.initEvent('frameconnectorstart', true, true); } this.windowSearch[wn].dispatchEvent(event);
}, this); }; Connector.prototype.init = function () { this.connectionStart = document.createEvent('Event'); this.connectionStart.initEvent('frameconnectorstart', true, true); this.walkFrames(); };

Connector.prototype.walkFrames = function (w) { var i, framesLength, frameAccessElem, currentFrame; if (!w) { w = top; } framesLength = w.frames.length; for (i = 0; i < framesLength; i++) { currentFrame = w.frames[i]; try { frameAccessElem = currentFrame.document; } catch (e) {} if (frameAccessElem && currentFrame.name == this.name) { _loadedCounter++; this.windowSearch[_loadedCounter] = currentFrame; if (_loadedCounter === this.windowCount) { this.finalize(); } } if (currentFrame.frames.length > 0) { this.walkFrames(currentFrame); } } }; window.addEventListener('frameconnectorstart', function () { window.localConnectStart() && localConnectStart(); });

'use strict'; window.addEventListener('DOMContentLoaded', function () { window.myLocalConnect = new Connector({ connectionName: 'MyConnectionID', windowName: 'firstElement', windowCount: 3 }); window.myLocalConnect.init(); });

function localConnectStart(){ //startAd(); }

// Nicht nötig aber Zu deiner Frage: (frameAccessElem && currentFrame[this.name])) --> hier ist this.name undefined und so kommt er da nicht weiter. In dieser Abfrage wird nur geprüft ob es einen frameAccessElem(document) gibt und ob es das currentFrame den Namen bzw. dein conectionName hat und wenn ja wird der currentFrame in ein Object gespeichert und daran später zu triggern. Im Anschluss wird geprüft ob die maximale Anzahl von Iframes mit der ConnectionID schon erreicht ist.

Viele Grüße Sebastian

leolo69 commented 3 years ago

Hallo Sebastian,

danke für die ausführliche Antwort. Ein befreundeter Programmierer hatte auch angefangen einige Sache zu verändern. Letztendlich habe ich aber darauf verzichtet, weil ich aus einem Canvasobject (Adobe Animate CC) Localconnect steuern mußte und ich das nicht hinbekommen habe. Jedenfalls nicht in dem Zeitfenster. Vielleicht versuche ich das nochmal bei einem anderen Projekt. Gruß Alex

renebaudisch commented 3 years ago

Ich habe heute mit einer Creative Agentur diese Doku ausprobiert und festgestellt, dass die Referenz auf "frameAccessElem" fehlerhaft ist, da sie nicht bei jeder Iteration revidiert wurde und somit die vorherige Referenz weiter mitgeführt hat. Damit ergab der Check in der Zeile nach dem Catch einen false/positiv da "frameAccessElem" ja falsch truthy war. Ich habe die Doku entsprechend angepasst und würde mich über feedback freuen, ob das auch euer Problem löst.

leolo69 commented 3 years ago

Hallo zusammen, ich weiß nicht, wer hier im Projekt genau was macht. Bei mir ist wieder ein Homepagetakeover gelandet und der Vermarkter möchte gern die einzelnen Bestandteile physisch angeliefert haben. Also sitze ich wieder vor dem Script.

Sebastian's Script: Wenn ich Sebastians Änderungen einbaue, dann schaffe ich es immer noch nicht eine Javascript-Funktion in window "A" von window "B" anzustossen. Wenn ich die Funktion superbanner.superbanner_control(); aufrufe, dann sagt mit die Console (Uncaught ReferenceError: superbanner is not defined). Der Banner hat sich eigetnlich über "window.myLocalConnect = new Connector({ connectionName: 'MyConnectionID',windowName: 'superbanner',windowCount: 2});" angemeldet und läuft auch in die localConnectStart-Funktion.

Rene's Script Wenn ich das neue Script von Rene einsetze, dann wirft er mir schon beim Initialisieren des zweiten iframes-connects einen Fehler aus. (Failed to execute 'dispatchEvent' on 'EventTarget': parameter 1 is not of type 'Event'.)

Vielleicht bin ich aber auch einfach nicht fähig. ;-) Lg Alex

smoremann commented 3 years ago

@leolo69 ich habe hier die Änderungen von Rene einmal aufgenommen.

var _loadedCounter = 0; 'use strict'; function Connector(conf) { window.name = this.name = conf.connectionName; window.windowName = this.windowName = conf.windowName; window.windowCount = this.windowCount = conf.windowCount; this.windows = {}; this.windowSearch = {};

} Connector.prototype.finalize = function () { Object.keys(this.windowSearch).forEach(function (wn) { this.windowSearch[wn].windows = this.windowSearch; var event; if(typeof(Event) === 'function') { event = new Event('frameconnectorstart'); }else{ event = document.createEvent('Event'); event.initEvent('frameconnectorstart', true, true); }

this.windowSearch[wn].dispatchEvent(event);    

}, this); }; Connector.prototype.walkFrames = function (w) { var i, framesLength, frameAccessElem, currentFrame; if (!w) { w = top; } framesLength = w.frames.length; for (i = 0; i < framesLength; i++) { frameAccessElem = ""; currentFrame = w.frames[i]; try { if (currentFrame.document.domain) { frameAccessElem = currentFrame.document; } } catch (e) {}

var _currentFrameName;      
try {
  _currentFrameName = currentFrame.name;
} catch (e) {       
}
if (frameAccessElem !="" && _currentFrameName == this.name) {
  _loadedCounter++;
  this.windowSearch[_loadedCounter] = currentFrame;
  if (_loadedCounter === this.windowCount) {
    this.finalize();
  }
}
if (currentFrame.frames.length > 0) {
  this.walkFrames(currentFrame);
}

} }; Connector.prototype.init = function () { this.connectionStart = document.createEvent('Event'); this.connectionStart.initEvent('frameconnectorstart', true, true); this.walkFrames(); };

window.addEventListener('frameconnectorstart', function () { window.localConnectStart() && localConnectStart(); });

'use strict'; window.addEventListener('DOMContentLoaded', function () { window.myLocalConnect = new Connector({ connectionName: 'MySpecialTestID', windowName: 'elementOne', windowCount: 3 }); TweenMax.delayedCall(1,startInit);

}); function startInit(){ window.myLocalConnect.init(); } function localConnectStart(){ console.log("CALLED IN ALL ADS IF LOCAL CONNECTION IS STARTED") }

Und dir ein console.log reingeschrieben. Ich brauchte nur den gemeinsamen Start der Werbemittel zu gewährleisten. Da dann eine timeline in allen Werbemittel zeitgleich gestartet wird.

Wenn du jetzt unterschiedliche Funktionen in den Werbemittel rufen möchtest, musst du erstmal in jedem Werbemittel einen EventListener("MeineSuperFunktionWerbemittel1") zur gewünschten Funktion schreiben und dieses Event dann an einem oder an alle Iframes schicken. So wie es ja 'frameconnectorstart' auch macht. Also relativ einfach :)

Du kannst da dann auch Objekte mit Infos schicken, falls du in dem anderen Werbemittel dann infos aus dem aktiven Werbemittel brauchst.

Ich würde einfach einen "XYZ"-EventListener in allen Werbemittel abfangen und dann entsprechend etwas tun oder ignorieren :)

Viele Grüße Sebastian

renebaudisch commented 3 years ago

Der dispatchEvent Error passiert in der finalize. Ich habe den Code nicht geschrieben, nur nachdem jetzt die var initialisiert wird, kommt er überhaupt erst zur finalize.

leolo69 commented 3 years ago

Dank euch beiden und vor allem Dir Sebastian für die wieder sehr ausführliche Antwort. Bei Dir klingt es alles so einfach. ;-) Werde mal versuchen, was ich da gebastelt bekomme.

Sehr, sehr nett von euch!!! LG Alex

leolo69 commented 3 years ago

Hi zusammen,

wenn es zu viel ist und ihr keine Zeit habt, dann einfach bitte bescheid geben...

1.) Ich habe pro Banner jeweils 2 Consolenausgaben über "frameconnectorstart". Hängt sicher damit zusammen, daß der Event sowohl in der init als auch in der finalize-Protypefuntktion definiert wird, gell?

2.) Das mit dem Eventbasteln ... will nicht so richtig.

Habe mal das gemacht: Connector.prototype.init = function () { this.connectionStart = document.createEvent('Event'); this.connectionStart.initEvent('frameconnectorstart', true, true);

this.communicationStart = document.createEvent('Event'); this.communicationStart.initEvent('comstart', true, true);

this.walkFrames(); };

window.addEventListener('frameconnectorstart', function () { window.localConnectStart() && localConnectStart(); });

window.addEventListener('comstart', function () { console.log("comstart"); });

function localConnectStart() {

    console.log("superbanner ready!");
  **gsap.delayedCall(5,function(){

 window.dispatchEvent(communicationStart);

window.dispatchEvent(comstart);

});**

}

Er erkennt aber weder "comstart" noch "communicationStart"

Was mache ich falsch. Danke Alex

smoremann commented 3 years ago

@leolo69 Das Problem ist das du das Event an die Windows der anderen Iframes senden musst. Das hier (window.dispatchEvent(comstart);) würde das Event nur an dein eigenes Window schicken.

Deshalb läuft diese Funktion über alle gespeichert Windows der Iframes die das Script vorher gespeichert hat, da sie die gleiche /selbe ConnectionID besitzen.

Connector.prototype.myComStart = function () { Object.keys(this.windowSearch).forEach(function (wn) { this.windowSearch[wn].windows = this.windowSearch; var event; if(typeof(Event) === 'function') { event = new Event('communicationStart'); }else{ event = document.createEvent('Event'); event.initEvent('communicationStart', true, true); }

this.windowSearch[wn].dispatchEvent(event);    

}, this); };

jetzt kannst du this.myComStart() nutzen um an alle Iframes das "communicationStart" event zu schicken.

Wenn du jetzt nur an ein Windo schicken willst musst du dir das aus dem this.windowSearch raussuchen, da sind die anderen Windows deiner Kampagne drin abgelegt.

Bin gespannt ob es hilft ;)

leolo69 commented 3 years ago

Es hilft definitiv. Danke. Und wenn ich jetzt an alle immer eine Nachricht schicken würde wollen. Wäre das hier falsch? Connector.prototype.myComStart = function (_st) { Object.keys(this.windowSearch).forEach(function (wn) { this.windowSearch[wn].windows = this.windowSearch; var event; if(typeof(Event) === 'function') { event = new Event('communicationStart'); }else{ event = document.createEvent('Event'); event.initEvent('communicationStart', true, true); } event.name = _st; this.windowSearch[wn].dispatchEvent(event);
}, this); };

window.addEventListener('communicationStart', function (e) { console.log("super: "+e.name); });

Es geht mir darum, daß ich dann "STEP1", "STEP2", usw. einfach an alle schicke und jeder Banner macht dann mit der Nachricht, was er will. Die Nachricht habe ich einfach als "name" reingepackt. Aber das siehst du ja eh direkt. Ich sollte lieber bei einfachen Bannern bleiben.

smoremann commented 3 years ago

@leolo69 Nein das wäre nicht falsch, aber damit überschreibst du das name property. Das würde an alle deine IFrames/Werbemittel das Event schicken.

Du kannst an einem CustomEvent Informationen anhängen die du dann wieder auslesen kannst. Das kann ein String sein oder auch Objekte{step:"1", timestamp:13241234} oder sowas :)

event.initEvent('communicationStart', true, true, "STEP1"); oder in deinem Fall dann: event.initEvent('communicationStart', true, true, _st);

Du solltest dir das gesamte (e) loggen um zu sehen was da alles drin ist. Da sollte dann auch deine StepInfo drin stehen.

Viele Grüße

leolo69 commented 3 years ago

Hallo Sebastian, ich habe es hingekriegt. Ohne deine Hilfe wäre ich aber nie soweit gekommen. Nochmals vielen lieben Dank für deine Unterstützung und einen schönen Feierabend. Alex