DIV innerText fills but not trigger the change event
Solution 1:
I've tried to replicated web.whatsapp.com
. As mentioned above by myf for the solution need those attributes <div role="textbox" contenteditable=true>
and the change
event not fire in that case.
The input
event works and for intercept the string we can to use event.target.textContent
instead event.target.value
.
document.addEventListener('DOMContentLoaded', function () {
const searchBar = document.querySelector('.search-bar');
const input = document.querySelector('.search-input');
const clearButton = document.querySelector('.clear-button');
const focusedField = () => {
searchBar.classList.add('focused');
input.focus();
};
const outSideClick = ev => {
ev.stopPropagation();
const isSearchBar = ev.target.closest('.search-bar');
const isEmptyField = input.textContent.length;
if (!isSearchBar && isEmptyField === 0) {
searchBar.classList.remove('focused');
document.removeEventListener('click', outSideClick);
}
};
const showClearBtn = ev => {
const isEmptyFiled = ev.target.textContent.length;
console.log(ev.target.textContent);
if (isEmptyFiled === 0) {
clearButton.classList.remove('active');
return;
}
clearButton.classList.add('active');
};
const clearText = () => {
input.textContent = '';
clearButton.classList.remove('active');
};
clearButton.addEventListener('click', clearText);
input.addEventListener('input', showClearBtn);
searchBar.addEventListener('mouseup', focusedField);
document.addEventListener('mouseup', outSideClick);
});
*,
::after,
::before {
margin: 0;
padding: 0;
box-sizing: border-box;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell,
'Open Sans', 'Helvetica Neue', sans-serif;
}
:root {
--bg: hsl(201, 27%, 10%);
--input-field: hsl(197, 7%, 21%);
--font-color: hsl(206, 3%, 52%);
--search-bar-height: 48px;
}
html {
height: 100%;
}
body {
width: 100%;
height: 100%;
display: flex;
justify-content: center;
align-items: center;
background-color: var(--bg);
}
.search-bar {
height: var(--search-bar-height);
width: 350px;
display: flex;
align-items: center;
position: relative;
padding-inline: 1em 1.5em;
overflow: hidden;
background-color: var(--input-field);
border-radius: 50px;
z-index: 10;
}
.search-bar.focused .icon[data-icon='search'] {
opacity: 0;
transform: rotate(45deg);
}
.search-bar.focused .icon[data-icon='back'] {
opacity: 1;
transform: rotate(0deg);
}
.search-bar.focused .search-placeholder {
display: none;
}
.search-button,
.clear-button {
width: calc(var(--search-bar-height) / 2);
height: calc(var(--search-bar-height) / 2);
display: flex;
position: relative;
background-color: transparent;
border: none;
outline: none;
transition: opacity 0.3s ease-in-out;
}
.icon {
position: absolute;
inset: 0;
transition: all 0.3s ease-in-out;
cursor: pointer;
}
.icon path {
fill: var(--font-color);
}
.icon[data-icon='back'] {
transform: rotate(-45deg);
opacity: 0;
}
.clear-button {
opacity: 0;
pointer-events: none;
}
.clear-button.active {
opacity: 1;
pointer-events: initial;
}
.search-field {
height: 2em;
display: flex;
margin-inline-start: 1em;
flex-grow: 1;
position: relative;
overflow: hidden;
}
.search-placeholder,
.search-input {
height: inherit;
position: absolute;
top: 3px;
font-size: 1rem;
color: var(--font-color);
transition: all 0.3s ease-in-out;
white-space: nowrap;
}
.search-placeholder {
text-overflow: ellipsis;
pointer-events: none;
user-select: none;
}
.search-input {
width: 100%;
outline: none;
border: 1px solid transparent;
/* border transparent for caret visibility */
}
<div class="search-bar" tabindex="1">
<button class="search-button">
<span class="icon" data-icon="search">
<svg viewBox="0 0 24 24" width="24" height="24">
<path
fill="currentColor"
d="M15.009 13.805h-.636l-.22-.219a5.184 5.184 0 0 0 1.256-3.386 5.207 5.207 0 1 0-5.207 5.208 5.183 5.183 0 0 0 3.385-1.255l.221.22v.635l4.004 3.999 1.194-1.195-3.997-4.007zm-4.808 0a3.605 3.605 0 1 1 0-7.21 3.605 3.605 0 0 1 0 7.21z"
></path>
</svg>
</span>
<span class="icon" data-icon="back">
<svg viewBox="0 0 24 24" width="24" height="24">
<path
fill="currentColor"
d="M12 4l1.4 1.4L7.8 11H20v2H7.8l5.6 5.6L12 20l-8-8 8-8z"
></path>
</svg>
</span>
</button>
<div class="search-field">
<div class="search-input" role="textbox" dir="ltr" tabindex="1" contenteditable="true"></div>
<div class="search-placeholder">Search or start new chat</div>
</div>
<button class="clear-button">
<span class="icon" data-icon="clear">
<svg viewBox="0 0 24 24" width="24" height="24">
<path
fill="currentColor"
d="M17.25 7.8L16.2 6.75l-4.2 4.2-4.2-4.2L6.75 7.8l4.2 4.2-4.2 4.2 1.05 1.05 4.2-4.2 4.2 4.2 1.05-1.05-4.2-4.2 4.2-4.2z"
></path>
</svg>
</span>
</button>
</div>
Solution 2:
onChange event triggers only for these supported elements:
<input type="checkbox">, <input type="color">, <input type="date">,
<input type="datetime">, <input type="email">, <input type="file">,
<input type="month">, <input type="number">, <input type="password">,
<input type="radio">, <input type="range">, <input type="search">,
<input type="tel">, <input type="text">, <input type="time">,
<input type="url">, <input type="week">, <select> and <textarea>
source: https://www.w3schools.com/jsref/event_onchange.asp
WhatsApp is built by ReactJS for web and ReactNative for mobile apps.
React has a event binder that reacts to certain changes and feeds back to the DOM.
If you're trying to recreate it using vanilla javascript, use the input tag