Skip to content

Commit d1ba90d

Browse files
authored
Merge pull request #59 from UMB-CS-682-Team-03/fix_browser_strangeness
addresses the different behaviors of the browsers when opening a popup and populating the popup DOM through the main window. In Linux Firefox and Linux Chrome, this behavior deferred as popup.document.readyState was not complete in Firefox while it was in Linux Chrome. While developing I primarily used Firefox as the main browser neglecting to verify in Chrome resulting in a solution of using DOM event "load" to populate the popup with javascript-generated DOM. very late into the project, this issue was found in some browsers getting the load event handler working while some browsers were left with blank popup. (windows chrome, edge, brave, linux chrome). The best possible guess I think is the speed of the js executed in Chrome V8 resulting in the load event being already fired when the event handler was successfully attached to listen for "load" event. To solve this bizarre case the merge request in openPopup method generates a document fragment and dispatches customEvent to replace the empty popup DOM with one inside the document Fragment. if popup document readyState is not "completed" the load event will be fired next (MDN)[https://developer.mozilla.org/en-US/docs/Web/API/Document/readyState#complete] multiple event being fired is possible when "load" event callback was executed before the manually dispatching of custom event but, the subsequent call will have the documentFragment content consumed and the event handler is just a no-op.
2 parents 290d310 + 1615924 commit d1ba90d

File tree

1 file changed

+64
-34
lines changed

1 file changed

+64
-34
lines changed

html/classhelper.js

Lines changed: 64 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,14 @@ class ClassHelper extends HTMLElement {
178178
});
179179
};
180180

181+
const handlePopupReadyEvent = (event) => {
182+
// we get a document Fragment in event.detail we replace it with the root
183+
// replaceChild method consumes the documentFragment content, subsequent calls will be no-op.
184+
if (e.detail.childElementCount === 1) {
185+
this.popupRef.document.replaceChild(event.detail, this.popupRef.document.documentElement);
186+
}
187+
}
188+
181189
const handleNextPageEvent = (event) => {
182190
this.pageChange(event.detail.value, this.helpurlProps)
183191
.catch(error => {
@@ -233,6 +241,7 @@ class ClassHelper extends HTMLElement {
233241
}
234242

235243
this.addEventListener("click", handleClickEvent);
244+
this.addEventListener("popupReady", handlePopupReadyEvent);
236245
this.addEventListener("prevPage", handlePrevPageEvent);
237246
this.addEventListener("nextPage", handleNextPageEvent);
238247
this.addEventListener("valueSelected", handleValueSelectedEvent);
@@ -303,7 +312,7 @@ class ClassHelper extends HTMLElement {
303312
throw new Error(message);
304313
}
305314

306-
ClassHelper.translations = translations;
315+
ClassHelper.translations = json;
307316
}
308317

309318
async fetchDropdownsData() {
@@ -904,49 +913,70 @@ class ClassHelper extends HTMLElement {
904913
throw new Error("Browser Failed to open Popup Window");
905914
}
906915

907-
this.popupRef.addEventListener("load", (event) => {
908-
const doc = event.target;
909-
const body = doc.body;
916+
// Create the popup root level page
917+
const page = document.createDocumentFragment();
918+
const html = document.createElement("html");
919+
const head = document.createElement("head");
920+
const body = document.createElement("body");
910921

911-
const itemDesignator = window.location.pathname.split("/").at(-1);
912-
let title = `${itemDesignator} - Classhelper`;
922+
body.classList.add("flex-container");
913923

914-
if (props.formProperty) {
915-
const label = document.getElementsByName(props.formProperty).item(0).parentElement.previousElementSibling;
916-
title = label.textContent + " - " + title;
917-
}
924+
const itemDesignator = window.location.pathname.split("/").at(-1);
925+
let titleText = `${itemDesignator} - Classhelper`;
926+
if (props.formName) {
927+
// main window lookup for the label of the form property
928+
const label = document.getElementsByName(props.formProperty).item(0).parentElement.previousElementSibling;
929+
titleText = label.textContent + " - " + titleText;
930+
}
918931

919-
doc.title = title;
932+
const titleTag = document.createElement("title");
933+
titleTag.textContent = titleText;
920934

921-
// Add external classhelper stylesheet to head
922-
const styleSheet = doc.createElement("link");
923-
styleSheet.rel = "stylesheet";
924-
styleSheet.type = "text/css";
925-
styleSheet.href = this.trackerBaseURL + '/' + CSS_STYLESHEET_FILE_NAME;
926-
doc.head.appendChild(styleSheet);
935+
const styleSheet = document.createElement("link");
936+
styleSheet.rel = "stylesheet";
937+
styleSheet.type = "text/css";
938+
styleSheet.href = this.trackerBaseURL + '/' + CSS_STYLESHEET_FILE_NAME;
927939

928-
body.classList.add("flex-container");
940+
head.appendChild(titleTag);
941+
head.appendChild(styleSheet);
929942

930-
if (this.dataset.searchWith) {
931-
const searchFrag = this.getSearchFragment(null);
932-
body.appendChild(searchFrag);
933-
}
943+
if (this.dataset.searchWith) {
944+
const searchFrag = this.getSearchFragment(null);
945+
body.appendChild(searchFrag);
946+
}
934947

935-
const paginationFrag = this.getPaginationFragment(prevPageURL, nextPageURL, props.pageIndex, props.pageSize, collection.length);
936-
body.appendChild(paginationFrag);
948+
const paginationFrag = this.getPaginationFragment(prevPageURL, nextPageURL, props.pageIndex, props.pageSize, collection.length);
949+
body.appendChild(paginationFrag);
937950

938-
const tableFrag = this.getTableFragment(props.fields, collection, preSelectedValues, !!props.formProperty);
939-
body.appendChild(tableFrag);
951+
const tableFrag = this.getTableFragment(props.fields, collection, preSelectedValues, !!props.formProperty);
952+
body.appendChild(tableFrag);
940953

941-
const separator = doc.createElement("div");
942-
separator.classList.add("separator");
943-
body.appendChild(separator);
954+
const separator = document.createElement("div");
955+
separator.classList.add("separator");
956+
body.appendChild(separator);
944957

945-
if (props.formProperty) {
946-
const accumulatorFrag = this.getAccumulatorFragment(preSelectedValues);
947-
body.appendChild(accumulatorFrag);
948-
}
949-
});
958+
if (props.formProperty) {
959+
const accumulatorFrag = this.getAccumulatorFragment(preSelectedValues);
960+
body.appendChild(accumulatorFrag);
961+
}
962+
963+
html.appendChild(head);
964+
html.appendChild(body);
965+
page.appendChild(html);
966+
967+
const dispatchPopupReady = () => this.dispatchEvent(new CustomEvent("popupReady", { detail: page }));
968+
969+
// Wait for the popup window to load, onload fire popupReady event on the classhelper
970+
this.popupRef.addEventListener("load", dispatchPopupReady);
971+
972+
// If load event was already fired way before the event listener was attached
973+
// we need to trigger it manually if popupRef is readyState complete
974+
if (this.popupRef.document.readyState === "complete") {
975+
dispatchPopupReady();
976+
// if we did successfully trigger the event, we can remove the event listener
977+
// else wait for it to be removed with closing of popup window, this cleaning up closure
978+
this.popupRef.removeEventListener("load", dispatchPopupReady);
979+
}
950980

951981
this.popupRef.addEventListener("keydown", (e) => {
952982
if (e.key === "ArrowDown") {

0 commit comments

Comments
 (0)