Skip to content

Commit

Permalink
Merge pull request #2 from hebrerillo/hide_float_element_when_clickin…
Browse files Browse the repository at this point in the history
…g_form

Hide float element when clicking a form element that triggers the mob…
  • Loading branch information
hebrerillo authored Dec 21, 2024
2 parents 3b61c4b + 8e3500f commit e0635b5
Show file tree
Hide file tree
Showing 3 changed files with 139 additions and 37 deletions.
83 changes: 64 additions & 19 deletions dist/index.bundle.js

Large diffs are not rendered by default.

12 changes: 12 additions & 0 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,18 @@
>
</div>
</div>
<div class="form__row">
<div class="form__block">
<label class="form__item-label">Height(cm): </label>
<input class="form__item" type="number" name="height" />
</div>
</div>
<div class="form__row">
<div class="form__block">
<label class="form__item-label">Weight(kilos): </label>
<input class="form__item" type="number" name="weight" />
</div>
</div>
<div class="form__row">
<div class="form__block">
<label class="form__item-label">Tell us more about you:</label>
Expand Down
81 changes: 63 additions & 18 deletions src/smart_float.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,15 @@ export class FloatElementHandler {
#formElement;
#formFieldset;
#isFormEndVisible;
#timeoutID;
#hiddenTimeoutID; //The timeout ID returned when hidding the floating element.
#isScrollCallbackExecuting;
#lastScrollTop;
constructor() {
this.#floatElement = document.querySelector("[data-float-element]");
this.#formElement = document.querySelector("[data-form-element]");
this.#formFieldset = this.#formElement.querySelector("fieldset");
this.#isFormEndVisible = false;
this.#timeoutID = -1;
this.#hiddenTimeoutID = -1;
this.#isScrollCallbackExecuting = false;
this.#lastScrollTop =
window.pageYOffset || document.documentElement.scrollTop;
Expand All @@ -29,11 +29,16 @@ export class FloatElementHandler {
initEvents() {
this.#formElement?.addEventListener("focusin", this.onFocusIn.bind(this));
this.#formElement?.addEventListener("focusout", this.onFocusOut.bind(this));

this.#formElement?.addEventListener("input", this.onInput.bind(this));

this.#formElement?.addEventListener("click", this.onFormClick.bind(this));
window.addEventListener("scroll", this.onScroll.bind(this));
this.configureFormEndObserver();
}

/**
* Configures an observer that will check the visibility of the end of the form.
*/
configureFormEndObserver() {
const floatElementHeight =
this.#floatElement?.getBoundingClientRect().height;
const observerOptions = {
Expand All @@ -60,11 +65,7 @@ export class FloatElementHandler {
onScroll() {
const scrollCallback = () => {
let st = window.pageYOffset || document.documentElement.scrollTop;
if (
st > this.#lastScrollTop &&
!this.#isFormEndVisible &&
this.hasFocusedElement()
) {
if (st > this.#lastScrollTop && this.hasFocusedElement()) {
this.hide();
} else if (st < this.#lastScrollTop) {
this.show();
Expand All @@ -82,17 +83,59 @@ export class FloatElementHandler {
this.#isScrollCallbackExecuting = true;
}

onInput(event) {
if (event.detail?.programatically || event.detail?.fromStorage) {
return;
/**
* Event function to handle click events on the form
*
* @param {Event} event
*/
onFormClick(event) {
const target = event.target;
if (this.isFormElementTriggeringKeyboard(target)) {
this.hide();
} else {
this.show();
}
}

/**
* Checks if focusing or clicking 'element' will cause the mobile keyboard
* to show up.
*
* @param {HTMLElement} element An HTML element (most likely, a HTMLInputElement)
* @returns {boolean} true if clicking or focusing on element will cause the mobile keyboard to show up, false otherwise
*/
isFormElementTriggeringKeyboard(element) {
const type = element.type ?? "";
switch (type) {
case "text":
case "password":
case "search":
case "email":
case "number":
case "tel":
case "url":
return true;
default:
return false;
}
}

/**
* Executed when there is an input on the form.
*/
onInput() {
this.hide();
}

/**
* Callback to be executed when the shipping methods are visible.
* Handles the visibility of the floating element when the end of the form is hidden/visible.
*
* If the end of the form is visible, the floating element should be visible. The height of the floating element is substracted
* so that it never hides any form input element.
*
* If the end of the form is not visible, the floating element should be hidden only if the focused element is a form element.
*
* @param {Array} entries
* @param {Array} entries The intersection entries.
*/
observerCallback(entries) {
entries.forEach((entry) => {
Expand Down Expand Up @@ -137,21 +180,23 @@ export class FloatElementHandler {
}

/**
* Shows the summary event
* Shows the floating event
*/
show() {
this.#floatElement?.classList.remove("form__float-submit--hidden");
}

/**
* Hides the summary element
* Hides the floating element only if the end of the form is not visible.
* The floating element is not hidden forever. If there is an inactivity of FLOAT_ELEMENT_HIDDEN_TIEMOUT milliseconds, the
* floating element will be shown again.
*/
hide() {
if (this.#isFormEndVisible) {
return;
}
window.clearTimeout(this.#timeoutID);
this.#timeoutID = window.setTimeout(
window.clearTimeout(this.#hiddenTimeoutID);
this.#hiddenTimeoutID = window.setTimeout(
this.show.bind(this),
FLOAT_ELEMENT_HIDDEN_TIEMOUT,
);
Expand Down

0 comments on commit e0635b5

Please sign in to comment.