diff --git a/examples/laravel/templates/standard.blade.php b/examples/laravel/templates/standard.blade.php
index 8408280..37853b8 100644
--- a/examples/laravel/templates/standard.blade.php
+++ b/examples/laravel/templates/standard.blade.php
@@ -37,10 +37,8 @@
{{-- 2ND PAGE CUSTOMIZE PANEL --}}
-
- {{-- COOKIES LIST --}}
-
-
+ {{-- COOKIES LIST --}}
+
{{-- NAV BAR --}}
diff --git a/examples/wordpress/templates/standard.php b/examples/wordpress/templates/standard.php
index f5f3240..bfea710 100644
--- a/examples/wordpress/templates/standard.php
+++ b/examples/wordpress/templates/standard.php
@@ -37,10 +37,8 @@
diff --git a/src/scss/themes/standard.scss b/src/scss/themes/standard.scss
index e223a3b..cf4d28d 100644
--- a/src/scss/themes/standard.scss
+++ b/src/scss/themes/standard.scss
@@ -372,49 +372,47 @@ $cn-w-modal-width: $cn-cookie-modal-width;
// * 2nd Page MODAL
.cn-modal-container {
- .cn-modal-wrapper {
- position: relative;
+ position: relative;
+ display: flex;
+ overflow: auto;
+ flex-direction: column;
+ align-items: center;
+
+ ul {
display: flex;
- overflow: auto;
flex-direction: column;
+ padding: 0;
+ margin: 0;
+ list-style: none;
+ }
+
+ .cn-cookie-wrapper {
+ display: flex;
+ flex-direction: row;
align-items: center;
+ justify-content: space-between;
+ padding: 20px;
+ border: 1px solid #{$ck-bgcolor-list};
+ border-radius: 20px;
+ margin: 10px 0;
+ }
- ul {
- display: flex;
- flex-direction: column;
- padding: 0;
- margin: 0;
- list-style: none;
- }
+ /* stylelint-disable-next-line no-descending-specificity */
+ span {
+ color: $ck-color-list;
+ font-family: $ck-font-family-list;
+ font-size: $ck-font-size-list;
+ }
- .cn-cookie-wrapper {
- display: flex;
- flex-direction: row;
- align-items: center;
- justify-content: space-between;
- padding: 20px;
- border: 1px solid #{$ck-bgcolor-list};
- border-radius: 20px;
- margin: 10px 0;
- }
+ a {
+ font-family: $ck-font-family-list;
+ font-size: $ck-font-size-list;
+ }
- /* stylelint-disable-next-line no-descending-specificity */
- span {
- color: $ck-color-list;
- font-family: $ck-font-family-list;
- font-size: $ck-font-size-list;
- }
-
- a {
- font-family: $ck-font-family-list;
- font-size: $ck-font-size-list;
- }
-
- label {
- color: $ck-color-list;
- font-family: $ck-font-family-list;
- font-size: $ck-font-size-list;
- }
+ label {
+ color: $ck-color-list;
+ font-family: $ck-font-family-list;
+ font-size: $ck-font-size-list;
}
.cn-cookie {
diff --git a/src/ts/index.ts b/src/ts/index.ts
index 2bea15b..220b2ae 100644
--- a/src/ts/index.ts
+++ b/src/ts/index.ts
@@ -1,6 +1,7 @@
/** 5.0.4 */
export default class CookieNotice {
+ /** The CN cookie status */
browserCookieName = "cn-init";
/** Cookie life time */
@@ -18,26 +19,43 @@ export default class CookieNotice {
/** Cookie expiration date as specified in the cookie itself */
cookieExpirationDate: Date;
+ /** Holding all cookie configs */
config: Record;
- modalBg: HTMLDivElement;
- cookieContainer: HTMLDivElement;
- bannerContainer: HTMLDivElement;
- modalContainer: HTMLDivElement;
- cookieShowModalBtn: HTMLButtonElement;
- cookieCloseModalBtn: HTMLButtonElement;
+ /** Prevent user from closing modal */
+ restrictCloseModal: boolean = true;
- bannerContent: HTMLDivElement;
- acceptAllBtn: HTMLButtonElement;
- denyAllBtn: HTMLButtonElement;
- customizeBtn: HTMLButtonElement;
- cookiesUnorderedList: HTMLUListElement;
- cookieLinkShowCustomizeModalList: NodeListOf;
- modalWrapper: HTMLDivElement;
- template: HTMLDivElement;
-
- restrictCloseModal: boolean;
+ // * -- DOM ELEMENTS
+ domElList: {
+ /** Modal transparent background => div.cn-modal-bg */
+ modalBg?: HTMLDivElement|null,
+ /** Modal window container for the cookie panel and button => div#cn-wrapper */
+ cookieContainer?: HTMLDivElement|null|null,
+ /** Modal container (wrapping pages) => div#cn-banner */
+ bannerContainer?: HTMLDivElement|null,
+ /** Modal banner (1rst page) container => div.cn-banner-content */
+ bannerContent?: HTMLDivElement|null,
+ /** Modal cookie list (2nd page) container => div.cn-modal-container */
+ modalContainer?: HTMLDivElement | null,
+ /** The big cookie button => button#cn-cookie-btn */
+ cookieShowModalBtn?: HTMLButtonElement | null,
+ /** Modal container close button => button#cn-close-modal */
+ cookieCloseModalBtn?: HTMLButtonElement | null,
+ /** Modal container accept all cookies button => button#cn-accept-banner */
+ acceptAllBtn?: HTMLButtonElement|null,
+ /** Modal container deny all cookies button => button#cn-deny-banner */
+ denyAllBtn?: HTMLButtonElement | null,
+ /** Modal container switch page / (validate and) close button => button#cn-settings-banner */
+ customizeBtn?: HTMLButtonElement | null,
+ /** Modal unordered cookie list => ul#cn-modal-list */
+ cookiesUnorderedList?: HTMLUListElement | null,
+ /** List of all links that will open cookie notice */
+ cookieLinkShowCustomizeModalList?: NodeListOf | null,
+ /** Cookie template for unordered cookie list */
+ template?: HTMLDivElement|null
+ } = {};
+ // * -- END DOM ELEMENTS
constructor(config: Record) {
// * Setup Config
@@ -51,137 +69,8 @@ export default class CookieNotice {
}
this.cookieExpirationDate = cookieExpirationDate;
- // * Inject DOM modal BG
- const modalBg = document.createElement("div");
- modalBg.classList.add("cn-modal-bg");
- document.body.append(modalBg);
- this.modalBg = modalBg;
-
- // * Setup Dom Elements
- const cookieContainer = document.querySelector(
- "div#cn-wrapper"
- ) as HTMLDivElement | null,
- cookieShowModalBtn = document.querySelector(
- "button#cn-cookie-btn"
- ) as HTMLButtonElement | null,
- cookieCloseModalBtn = document.querySelector(
- "button#cn-close-modal"
- ) as HTMLButtonElement | null,
- cookieLinkShowCustomizeModalPageList = document.querySelectorAll(
- ".cn-cookie-link"
- ) as NodeListOf,
- bannerContainer = document.querySelector(
- "div#cn-banner"
- ) as HTMLDivElement | null,
- bannerContent = document.querySelector(
- "div.cn-banner-content"
- ) as HTMLDivElement | null,
- modalContainer = document.querySelector(
- "div.cn-modal-container"
- ) as HTMLDivElement | null,
- acceptAllBtn = document.querySelector(
- "button#cn-accept-banner"
- ) as HTMLButtonElement | null,
- denyAllBtn = document.querySelector(
- "button#cn-deny-banner"
- ) as HTMLButtonElement | null,
- customizeBtn = document.querySelector(
- "button#cn-settings-banner"
- ) as HTMLButtonElement | null,
- cookiesUnorderedList = document.querySelector(
- "ul#cn-modal-list"
- ) as HTMLUListElement | null,
- modalWrapper = document.querySelector(
- "div.cn-modal-wrapper"
- ) as HTMLDivElement,
- template = document.querySelector(
- "div#cn-li-template"
- ) as HTMLDivElement | null;
- if (!cookieContainer) {
- throw new Error("Cannot find div#cn-wrapper");
- }
- if (!cookieShowModalBtn) {
- throw new Error("Cannot find 'button#cn-cookie-btn' element");
- }
- if (!cookieCloseModalBtn) {
- throw new Error("Cannot find 'button#cn-close-modal' element");
- }
- if (!bannerContainer) {
- throw new Error("Cannot find 'div#cn-banner' element");
- }
- if (!bannerContent) {
- throw new Error("Cannot find 'div.cn-banner-content' element");
- }
- if (!modalContainer) {
- throw new Error("Cannot find 'div.cn-modal-container' element");
- }
- if (!acceptAllBtn) {
- throw new Error("Cannot find 'button#cn-accept-banner' element");
- }
- if (!denyAllBtn) {
- throw new Error("Cannot find 'button#cn-deny-banner' element");
- }
- if (!customizeBtn) {
- throw new Error("Cannot find 'button.cn-settings-banner' element");
- }
- if (!cookiesUnorderedList) {
- throw new Error("Cannot find 'ul#cn-modal-list' element");
- }
- if (!modalWrapper) {
- throw new Error("Cannot find 'div.cn-modal-wrapper' element");
- }
- if (!template) {
- throw new Error("Cannot find 'div#cn-li-template' element");
- }
-
- this.cookieShowModalBtn = cookieShowModalBtn;
- this.cookieCloseModalBtn = cookieCloseModalBtn;
- this.cookieLinkShowCustomizeModalList =
- cookieLinkShowCustomizeModalPageList;
- this.cookieContainer = cookieContainer;
- this.bannerContainer = bannerContainer;
- this.bannerContent = bannerContent;
- this.cookiesUnorderedList = cookiesUnorderedList;
- this.modalWrapper = modalWrapper;
- this.modalContainer = modalContainer;
- this.acceptAllBtn = acceptAllBtn;
- this.denyAllBtn = denyAllBtn;
- this.customizeBtn = customizeBtn;
-
- this.template = template;
- const memoryTemplate = this.template.cloneNode(true) as HTMLDivElement;
- this.template.remove();
- this.template = memoryTemplate;
-
- this.restrictCloseModal = true;
-
- // * Setup Events
- this.addEventPrevent(this.modalBg, "click", this.closeCookieModal);
- this.addEventPrevent(
- this.cookieCloseModalBtn,
- "click",
- this.closeCookieModal
- );
- this.addEventPrevent(this.acceptAllBtn, "click", this.acceptAllCookies);
- this.addEventPrevent(this.denyAllBtn, "click", this.denyAllCookies);
- this.addEventPrevent(
- this.customizeBtn,
- "click",
- this.switchToModalPageOrClose
- );
- this.addEventPrevent(
- this.cookieShowModalBtn,
- "click",
- this.toggleCookieModal
- );
- this.cookieLinkShowCustomizeModalList.forEach((cookieLink) => {
- this.addEventPrevent(
- cookieLink,
- "click",
- this.openCookieModalToCookiePage
- );
- this.addEventKbdValidate(cookieLink, this.openCookieModalToCookiePage);
- });
+ this.initDomElements();
+ this.setupEvents();
// * Setup CN Cookie
if (!this.cookiesMatchesConfig() || this.cookiesOlderThan(new Date())) {
@@ -221,9 +110,11 @@ export default class CookieNotice {
* Update restrict for closing cookie modal
*/
updateRestrictCloseModal() {
+ const cookieCloseModalBtn = this.getDomEl("cookieCloseModalBtn"),
+ modalBg = this.getDomEl("modalBg");
this.restrictCloseModal = (Object.values(this.getCookieStatusList()).includes(CookieStatus.wait)) ? true : false;
- (this.restrictCloseModal) ? this.cookieCloseModalBtn.classList.add("d-none") : this.cookieCloseModalBtn.classList.remove("d-none");
- (this.restrictCloseModal) ? this.modalBg.classList.add("cn-modal-bg-active") : this.modalBg.classList.remove("cn-modal-bg-active");
+ (this.restrictCloseModal) ? cookieCloseModalBtn.classList.add("d-none") : cookieCloseModalBtn.classList.remove("d-none");
+ (this.restrictCloseModal) ? modalBg.classList.add("cn-modal-bg-active") : modalBg.classList.remove("cn-modal-bg-active");
}
/**
@@ -625,12 +516,12 @@ export default class CookieNotice {
openCookieModalToCookiePage() {
document.body.classList.add("cn-open");
this.resetCookieModal();
- this.bannerContent.classList.add("d-none");
- this.modalContainer.classList.remove("d-none");
- this.bannerContainer.classList.add("cn-modal-page");
+ this.getDomEl("bannerContent").classList.add("d-none");
+ this.getDomEl("modalContainer").classList.remove("d-none");
+ this.getDomEl("bannerContainer").classList.add("cn-modal-page");
// Scroll to the top of the cookies list
- this.modalWrapper.scrollTop = 0;
+ this.getDomEl("modalContainer").scrollTop = 0;
this.setupCookieModalList();
this.setTabIndex();
}
@@ -644,9 +535,9 @@ export default class CookieNotice {
document.body.classList.remove("cn-open");
if (!this.isOnBannerPage()) {
// * Make toggle page to banner on closing
- this.bannerContent.classList.toggle("d-none");
- this.modalContainer.classList.toggle("d-none");
- this.bannerContainer.classList.remove("cn-modal-page");
+ this.getDomEl("bannerContent").classList.toggle("d-none");
+ this.getDomEl("modalContainer").classList.toggle("d-none");
+ this.getDomEl("bannerContainer").classList.remove("cn-modal-page");
}
this.unsetTabIndex();
this.setupCookieModalList();
@@ -664,20 +555,20 @@ export default class CookieNotice {
setTimeout(() => {
const focusableSelector =
"a[href], button, [role=\"button\"], input, embed, object, iframe, textarea, select, details, [tabindex]:not([tabindex=\"-1\"])";
-
+ const cookieContainer = this.getDomEl("cookieContainer");
// * Only focusable elements
[...document.body.querySelectorAll(focusableSelector)]
.filter(
(el) =>
!el.hasAttribute("disabled") &&
!el.getAttribute("aria-hidden") &&
- !this.cookieContainer.contains(el)
+ !cookieContainer.contains(el)
)
.forEach((el) => {
(el as HTMLElement).setAttribute("tabindex", "-1");
(el as HTMLElement).classList.add("cn-disabled");
});
- [...this.cookieContainer.querySelectorAll(focusableSelector)]
+ [...cookieContainer.querySelectorAll(focusableSelector)]
.filter(
(el) =>
!el.hasAttribute("disabled") &&
@@ -688,13 +579,13 @@ export default class CookieNotice {
(el as HTMLElement).offsetHeight ||
el.getClientRects().length
) &&
- !this.cookieContainer.contains(el)
+ !cookieContainer.contains(el)
)
.forEach((el) => {
(el as HTMLElement).setAttribute("tabindex", "0");
(el as HTMLElement).classList.remove("cn-disabled");
});
- this.cookieShowModalBtn.setAttribute("tabindex", "-1");
+ this.getDomEl("cookieShowModalBtn").setAttribute("tabindex", "-1");
}, 500);
}
@@ -708,7 +599,7 @@ export default class CookieNotice {
(el as HTMLElement).setAttribute("tabindex", "0");
(el as HTMLElement).classList.remove("cn-disabled");
});
- this.cookieShowModalBtn.setAttribute("tabindex", "0");
+ this.getDomEl("cookieShowModalBtn").setAttribute("tabindex", "0");
}, 500);
}
@@ -716,23 +607,24 @@ export default class CookieNotice {
* Reset the modal view state
*/
resetCookieModal() {
- this.bannerContent.classList.remove("d-none");
- this.modalContainer.classList.add("d-none");
- this.bannerContainer.classList.remove("cn-modal-page");
- if (!this.customizeBtn.dataset.openModal) {
+ const customizeBtn = this.getDomEl("customizeBtn");
+ this.getDomEl("bannerContent").classList.remove("d-none");
+ this.getDomEl("modalContainer").classList.add("d-none");
+ this.getDomEl("bannerContainer").classList.remove("cn-modal-page");
+ if (!customizeBtn.dataset.openModal) {
throw new Error("Cannot find attribut data-open-modal");
}
- this.customizeBtn.innerHTML = this.customizeBtn.dataset.openModal;
+ customizeBtn.innerHTML = customizeBtn.dataset.openModal;
}
/**
* Set the cookies list in the modal
*/
setupCookieModalList() {
- this.cookiesUnorderedList.innerHTML = "";
+ this.getDomEl("cookiesUnorderedList").innerHTML = "";
Object.entries(this.config).forEach(([cookieName, cookieConfig]) => {
const cookie = document.createElement("li");
- const childItem = this.template.children.item(
+ const childItem = this.getDomEl("template").children.item(
0
) as HTMLDivElement | null;
if (childItem === null) {
@@ -784,7 +676,7 @@ export default class CookieNotice {
this.addEvent(cnSwitchInput, "click", () => toggleCookie(cookieName));
this.addEventKbdValidate(cnSwitch, () => cnSwitchInput.click());
- this.cookiesUnorderedList.append(cookie);
+ this.getDomEl("cookiesUnorderedList").append(cookie);
if (!this.hasBrowserCookie()) {
// * Ldcweb cookie is not set.
@@ -830,32 +722,34 @@ export default class CookieNotice {
* Toggle Cookie modal pages
*/
toggleCookieModalPages() {
- this.bannerContent.classList.toggle("d-none");
- this.modalContainer.classList.toggle("d-none");
- this.bannerContainer.classList.toggle("cn-modal-page");
+ this.getDomEl("bannerContent").classList.toggle("d-none");
+ this.getDomEl("modalContainer").classList.toggle("d-none");
+ this.getDomEl("bannerContainer").classList.toggle("cn-modal-page");
if (!this.isOnBannerPage()) {
- this.bannerContainer.classList.add("cn-modal-page");
+ this.getDomEl("bannerContainer").classList.add("cn-modal-page");
this.denyAllWaitingCookies();
}
this.setTabIndex();
this.setupCookieModalList();
- this.togglePersonalizeBtn();
+ this.toggleCustomizeBtn();
}
/**
* Toggle customize btn
*/
- togglePersonalizeBtn() {
- const customizeBtnBannerDataset = this.customizeBtn.dataset;
- this.modalWrapper.scrollTop = 0;
- if (this.bannerContent.classList.contains("d-none")) {
- this.customizeBtn.innerHTML =
+ toggleCustomizeBtn() {
+ const customizeBtn = this.getDomEl("customizeBtn"),
+ customizeBtnBannerDataset = this.getDomEl("customizeBtn").dataset;
+
+ this.getDomEl("modalContainer").scrollTop = 0;
+ if (this.getDomEl("bannerContent").classList.contains("d-none")) {
+ customizeBtn.innerHTML =
customizeBtnBannerDataset.openModal as string;
- this.customizeBtn.title = customizeBtnBannerDataset.openModal as string;
+ customizeBtn.title = customizeBtnBannerDataset.openModal as string;
} else {
- this.customizeBtn.innerHTML =
+ customizeBtn.innerHTML =
customizeBtnBannerDataset.closeModal as string;
- this.customizeBtn.title = customizeBtnBannerDataset.closeModal as string;
+ return customizeBtn.title = customizeBtnBannerDataset.closeModal as string;
}
}
@@ -863,7 +757,7 @@ export default class CookieNotice {
* Get the actual page: banner or modal (with cookie choices)
*/
isOnBannerPage(): boolean {
- return this.modalContainer.classList.contains("d-none");
+ return this.getDomEl("modalContainer").classList.contains("d-none");
}
/**
@@ -883,6 +777,76 @@ export default class CookieNotice {
throw new Error(`Un handled cookie status ${status}`);
}
}
+
+ /** Setup Events */
+ private setupEvents(): void {
+ this.addEventPrevent(this.getDomEl("modalBg"), "click", this.closeCookieModal);
+ this.addEventPrevent(this.getDomEl("cookieCloseModalBtn"), "click", this.closeCookieModal );
+ this.addEventPrevent(this.getDomEl("acceptAllBtn"), "click", this.acceptAllCookies);
+ this.addEventPrevent(this.getDomEl("denyAllBtn"), "click", this.denyAllCookies);
+ this.addEventPrevent(this.getDomEl("customizeBtn"), "click", this.switchToModalPageOrClose );
+ this.addEventPrevent(this.getDomEl("cookieShowModalBtn"), "click", this.toggleCookieModal );
+ this.getDomEl("cookieLinkShowCustomizeModalList").forEach((cookieLink) => {
+ this.addEventPrevent(
+ cookieLink,
+ "click",
+ this.openCookieModalToCookiePage
+ );
+ this.addEventKbdValidate(cookieLink, this.openCookieModalToCookiePage);
+ });
+ }
+
+ /** Get dom element */
+ private getDomEl>
+ (item: K): T {
+ // Exclude null|undefined as elements are verified in initDomElements
+ return this.domElList[item] as T;
+ }
+
+ /** Init Dom elements selector */
+ private initDomElements(): void {
+ // * Inject DOM modal BG
+ const modalBg = document.createElement("div");
+ modalBg.classList.add("cn-modal-bg");
+ document.body.append(modalBg);
+ this.domElList.modalBg = modalBg;
+
+ // * Setup Dom Elements
+ this.domElList.cookieLinkShowCustomizeModalList = document.querySelectorAll(
+ ".cn-cookie-link"
+ ) as NodeListOf;
+
+ const selectorList = {
+ "div#cn-wrapper": "cookieContainer",
+ "button#cn-cookie-btn": "cookieShowModalBtn",
+ "button#cn-close-modal": "cookieCloseModalBtn",
+ "div#cn-banner": "bannerContainer",
+ "div.cn-banner-content": "bannerContent",
+ "div.cn-modal-container": "modalContainer",
+ "button#cn-accept-banner": "acceptAllBtn",
+ "button#cn-deny-banner": "denyAllBtn",
+ "button#cn-settings-banner": "customizeBtn",
+ "ul#cn-modal-list": "cookiesUnorderedList",
+ "div#cn-li-template": "template",
+ };
+
+ Object.entries(selectorList).forEach(entry => {
+ const selector = entry[0],
+ domElItemKey = entry[1] as keyof typeof this.domElList;
+ let itemEntry = this.domElList[domElItemKey];
+ const domEl = document.querySelector(selector) as Exclude;
+ itemEntry = domEl as typeof itemEntry;
+ if (!itemEntry) {
+ throw new Error(`Cannot find dom element ${selector}`);
+ }
+ this.domElList = {...this.domElList, [domElItemKey]: itemEntry};
+ });
+
+ // * Clone and remove original template node
+ const memoryTemplate = this.domElList.template!.cloneNode(true) as HTMLDivElement;
+ this.domElList.template!.remove();
+ this.domElList.template = memoryTemplate;
+ }
}
enum CookieStatus {