I'm a javascript novice, and I have a problem that bothers me~
I often need to write a function to click a button to pop up a modal. The interaction logic of this function is repeated, but sometimes the UI or copywriting needs to be adjusted~
So I want to know How can such a repetitive function be packaged into a component, and other pages need to use modal-like functions in the future, which can be directly referenced and only need to change the copy without rewriting CSS / HTML / JavaScript?
// 匯出 excel 彈窗 JS
$("*[data-modal]").on("click", Modeal);
function Modeal() {
$(".excel_popup").css("display", "flex");
$("body").css("overflow", "hidden");
// 關閉彈窗
$(document).on("click", function(e) {
if (
e.target.className == "close" ||
e.target.className == "excel_popup" ||
e.target.className == "btn_confirm" ||
e.target.className == "btn_consider"
) {
$("#js-job_excel_popup").css("display", "none");
$("body").css("overflow", "auto");
}
});
}
.excel_popup {
position: fixed;
z-index: 999999999;
width: 100%;
height: 100vh;
top: 0;
left: 0;
right: 0;
bottom: 0;
margin: auto;
background-color: rgba(0, 0, 0, 0.3);
justify-content: center;
align-items: center;
display: none;
}
.excel_popup .excel_close_wrap {
width: 360px;
background-color: #fff;
padding: 16px 24px;
border-radius: 8px;
margin: 0 8px;
}
.excel_popup .excel_close_wrap header {
position: relative;
width: 100%;
display: flex;
justify-content: center;
align-items: center;
border-bottom: 1px solid #222;
padding-bottom: 16px;
}
.excel_popup .excel_close_wrap header h2 {
font-size: 18px;
line-height: 1.5;
font-weight: 500;
text-align: center;
}
.excel_popup .excel_close_wrap header .close {
position: absolute;
right: 0;
display: inline-block;
width: 20px;
height: 20px;
background-repeat: no-repeat;
background-size: contain;
cursor: pointer;
}
.excel_popup .excel_close_wrap .txt {
font-size: 16px;
font-weight: 400;
line-height: 1.5;
text-align: center;
padding: 32px 0px;
color: #222;
}
.excel_popup .excel_close_wrap .btn_group {
display: flex;
justify-content: space-between;
font-size: 14px;
line-height: 1.5;
letter-spacing: 0.15px;
}
.excel_popup .excel_close_wrap .btn_group .btn_consider {
width: 132px;
text-align: center;
padding: 8px;
border-radius: 4px;
border: 1px solid #222;
margin-right: 4px;
cursor: pointer;
}
.excel_popup .excel_close_wrap .btn_group .btn_consider:hover {
border: 1px solid #222;
}
.excel_popup .excel_close_wrap .btn_group .btn_confirm {
width: 132px;
text-align: center;
padding: 8px;
border-radius: 4px;
border: 1px solid #222;
background-color: #222;
margin-left: 4px;
cursor: pointer;
color: #fff;
}
.excel_popup .excel_close_wrap .btn_group .btn_confirm:hover {
background-color: yellow;
border: 1px solid #222;
color: #222;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<a class="excel_export" id="js-excel_export" href="javascript:;" data-modal="js-job_excel_popup">
<figure></figure>
<p>Click the popup</p>
</a>
<div class="excel_popup" id="js-job_excel_popup">
<div class="excel_close_wrap">
<header>
<h2>TITLE</h2>
<div class="close" id="js-close"></div>
</header>
<p class="txt">Hello World!!!</p>
<div class="btn_group">
<a class="btn_consider">Cancel</a>
<a class="btn_confirm">Send</a>
</div>
</div>
</div>
I have created several windows modal on my site which works perfectly on desktop. I would like to improve the user experience on mobile, by allowing the user to leave the modal window by clicking on the native "back" button of the mobile browser.
I've done some research on the web, and most of the time I find answers for React, or with libraries I don't use.
Do you have any idea how to make this possible in vanilla Javascript?
Thanks a lot for your help, here is the fiddle of my code
setupWaitingListModal();
function setupWaitingListModal() {
let waitingListLink = document.querySelector("#waitingList");
let waitingListModal = document.querySelector("#waitingListModal");
let waitingListCloseButton = document.querySelector('#waitingListCloseButton');
function toggleWaitingListModal() {
waitingListModal ? waitingListModal.classList.toggle("show-modal") : null;
body.classList.toggle("noscroll");
}
if (waitingListLink) {
waitingListLink.addEventListener("click", toggleWaitingListModal);
}
if (waitingListCloseButton) {
waitingListCloseButton.addEventListener('click', toggleWaitingListModal)
}
}
/* http://meyerweb.com/eric/tools/css/reset/
v2.0 | 20110126
License: none (public domain)
*/
html,
body,
div,
span,
applet,
object,
iframe,
h1,
h2,
h3,
h4,
h5,
h6,
p,
blockquote,
pre,
a,
abbr,
acronym,
address,
big,
cite,
code,
del,
dfn,
em,
img,
ins,
kbd,
q,
s,
samp,
small,
strike,
strong,
sub,
sup,
tt,
var,
b,
u,
i,
center,
dl,
dt,
dd,
ol,
ul,
li,
fieldset,
form,
label,
legend,
table,
caption,
tbody,
tfoot,
thead,
tr,
th,
td,
article,
aside,
canvas,
details,
embed,
figure,
figcaption,
footer,
header,
hgroup,
menu,
nav,
output,
ruby,
section,
summary,
time,
mark,
audio,
video {
margin: 0;
padding: 0;
border: 0;
font-size: 100%;
font: inherit;
vertical-align: baseline;
}
/* HTML5 display-role reset for older browsers */
article,
aside,
details,
figcaption,
figure,
footer,
header,
hgroup,
menu,
nav,
section {
display: block;
}
body {
line-height: 1;
}
ol,
ul {
list-style: none;
}
blockquote,
q {
quotes: none;
}
blockquote:before,
blockquote:after,
q:before,
q:after {
content: "";
content: none;
}
table {
border-collapse: collapse;
border-spacing: 0;
}
html {
font-family: "Circular Std";
-ms-text-size-adjust: 100%;
-webkit-text-size-adjust: 100%;
scroll-behavior: smooth !important;
}
body {
height: 100vh;
margin: 0;
background-color: white;
}
b {
font-weight: bold !important;
display: contents;
}
.primary-color {
color: #3ce7e3 !important;
}
.primary-color-dark {
color: #009aae !important;
}
.dark-grey {
color: #7384a7 !important;
}
.black {
color: #002832;
}
.dark-black {
color: black !important;
}
.width-100 {
width: 100% !important;
}
.padding-0 {
padding: 0 !important;
}
.padding-bottom-0 {
padding-bottom: 0 !important;
}
.padding-top-2 {
padding-top: 2rem;
}
.padding-top-10 {
padding-top: 10rem !important;
}
.padding-bottom-10 {
padding-bottom: 10rem !important;
}
.margin-left-1 {
margin-left: 1rem;
}
.margin-right-1 {
margin-right: 1rem;
}
.margin-top-1 {
margin-top: 1rem;
}
.margin-bottom-1 {
margin-bottom: 1rem;
}
.margin-left-2 {
margin-left: 2rem;
}
.margin-right-2 {
margin-right: 2rem;
}
.margin-top-2 {
margin-top: 2rem;
}
.margin-bottom-2 {
margin-bottom: 2rem;
}
.margin-top-4 {
margin-top: 4rem;
}
.margin-bottom-4 {
margin-bottom: 4rem;
}
.margin-top-6 {
margin-top: 6rem;
}
.margin-bottom-6 {
margin-bottom: 6rem;
}
.padding-bottom-8 {
padding-bottom: 8rem;
}
.padding-top-8 {
padding-top: 8rem;
}
.app-right-left-padding {
padding: 0 11.5%;
}
.font-weight-unset {
font-weight: unset !important;
}
.header-section {
padding-bottom: 4rem;
}
.banner {
background: #D7FFFE;
border-radius: 20px;
padding: 6px 0px;
color: #009AAE;
line-height: 20px;
width: 100%;
}
.error {
color: #d92f45;
display: flex;
padding-top: 8px;
}
.noscroll {
overflow: hidden;
}
.text-font-30 {
font-weight: bold;
font-size: 30px;
line-height: 36px;
letter-spacing: 0.3px;
}
.text-font-24 {
font-weight: bold;
font-size: 24px;
line-height: 30px;
letter-spacing: 0.3px;
}
.text-font-16 {
font-weight: normal;
font-size: 16px;
line-height: 22px;
letter-spacing: 0.3px;
}
/* required for wordpress START */
.max-width-100 {
max-width: 100% !important;
}
.site-info {
display: none;
}
.site-main {
margin: 0 !important;
padding: 0 !important;
}
#colophon {
display: none;
}
.cli-plugin-button {
border-radius: 8px !important;
}
#bitnami-banner {
display: none;
}
/* Cookie modal CSS override */
.cli-modal .cli-modal-close:hover {
background-color: rgb(0, 154, 174) !important;
}
.cli-tab-footer .wt-cli-privacy-accept-btn {
background-color: rgb(0, 154, 174) !important;
border-radius: 8px !important;
}
.cli-tab-footer .wt-cli-privacy-accept-btn:hover {
color: #ffffff !important;
}
.cli-switch input:checked + .cli-slider {
background-color: rgb(0, 154, 174) !important;
}
/* Cookie modal CSS override ends here*/
/* required for wordpress END */
header {
position: fixed;
display: flex;
align-items: center;
width: 100%;
height: 100px;
left: 0px;
top: 0px;
z-index: 9999999;
background: white;
}
.waiting-list-section {
padding-top: 15px;
}
.header-content {
padding: 0 11.5%;
display: flex;
flex-direction: column;
text-align: center;
align-items: center;
width: 100%;
justify-content: space-between;
}
.header-content .logo img {
height: 30px;
}
.logo-mobile {
visibility: hidden;
}
.header-content .menu-section {
display: flex;
flex-direction: row;
text-align: center;
align-items: center;
width: 100%;
justify-content: space-between;
margin-top: 15px;
}
.header-content .menu-section .link .current_page_item > a {
color: #3ce7e3;
}
.header-second-part {
width: 100%;
}
.third-part-header {
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
}
.language-button {
flex-direction: row;
display: flex;
align-items: center;
justify-content: center;
border: 0.5px solid #D4DAE6;
box-sizing: border-box;
border-radius: 20px;
padding: 3px 8px 3px 23px;
}
.Language {
display: none;
}
.lang-item {
list-style: none;
padding-right: 4px;
font-weight: bold;
font-size: 16px;
line-height: 20px;
padding-left: 5px;
border: none;
padding-top: 0;
}
.lang-item-first {
border: none;
}
.lang-item.current-lang {
display: none;
}
/* .header-content .menu-section .link {
margin-left: 3rem;
} */
/* New waiting list page ends here */
/* Dropdown menu starts here */
font-family: 'circular-std-book';
font-weight: bold;
color: #002832;
}
.wp-block-latest-posts .wp-block-latest-posts__post-excerpt, .wp-block-latest-posts .wp-block-latest-posts__post-full-content {
font-family: 'circular-std-book';
}
.columns-2 {
max-width: 730px;
margin-top: 3rem;
}
.blog-page-title {
padding-top: 10rem;
font-style: normal;
font-weight: bold;
font-size: 48px;
line-height: 60px;
letter-spacing: 0.3px;
color: #002832;
text-align: left;
max-width: 1110px;
margin: 0 auto;
}
.page-numbers {
margin-right: 10px;
}
.blog-page-subtitle {
font-size: 24px;
line-height: 30px;
letter-spacing: 0.3px;
color: #002832;
text-align: left;
max-width: 1100px;
margin: 2rem auto 0 auto;
border-bottom: 1px solid;
padding-bottom: 2rem;
}
.navigation .nav-links {
display:flex;
justify-content: center;
}
.pagination .nav-links > *:last-child {
margin-right: 5px;
margin-left: 5px;
}
.pagination .nav-links > * {
margin-right: 10px;
margin-left: 10px;
}
.pagination .nav-links > *.next {
margin-left: 0;
}
.pagination .nav-links > *.prev {
margin-right: 0;;
}
.wp-post-image {
border-radius: 20px;
width: 100%;
}
.authored-by {
padding-left: 20px;
color: #3c3d3e;
line-height: 28px;
font-size: 16px;
letter-spacing: -0.3px;
text-align: right;
}
.author {
font-size: 12px;
color: #009AAE;
}
.posted-on {
font-size: 11px;
line-height: 15px;
letter-spacing: -0.3px;
color: #7384A7;
border: 0.5px solid #D4DAE6;
box-sizing: border-box;
border-radius: 20px;
padding: 6px 12px 6px 12px;
margin-right: 10px;
}
.entry-date {
line-height: 15px;
letter-spacing: -0.3px;
color: #7384A7;
}
.span-reading-time {
font-size: 11px;
line-height: 15px;
letter-spacing: -0.3px;
color: #7384A7;
border: 0.5px solid #D4DAE6;
box-sizing: border-box;
border-radius: 20px;
padding: 6px 12px 6px 12px;
margin-right: 10px;
}
.type-post > .authored-by {
display: flex;
justify-content: flex-end;
font-size: 16px;
}
.tag-container {
padding-top: 5px;
}
.post-attribute {
position: absolute;
margin-top: 0;
bottom: 0;
display: flex;
flex-direction: row;
}
.excerpt-wrapper .entry-content > p {
font-size: 16px;
}
.post-attribute-page {
display: flex;
flex-direction: row;
justify-content: space-between;
max-width: 720px;
margin: 0 auto;
padding: 0 10px;
}
.mytag {
font-size: 11px;
line-height: 15px;
letter-spacing: -0.3px;
color: #7384A7;
border: 0.5px solid #D4DAE6;
box-sizing: border-box;
border-radius: 20px;
padding: 6px 12px 6px 12px;
background: #F2F4F8;
margin-right: 10px;
}
.button-redirection {
display: flex;
align-items: center;
justify-content: center;
font-weight: bold;
font-size: 18px;
line-height: 22px;
text-align: right;
letter-spacing: 0.3px;
color: #002832;
border: 1px solid #002832;
box-sizing: border-box;
border-radius: 10px;
padding: 12px 8px 13px 12px;
width: 200px;
margin: 0 auto;
}
.more-info-img {
vertical-align: bottom;
}
.vector {
vertical-align: middle;
}
#waitingListCloseButton {
cursor: pointer;
}
.waiting-list-modal-box {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
background-color: #ffffff;
padding: 1.5rem 2rem 1.5rem;
width: 790px;
border-radius: 20px;
box-shadow: 2px 2px 10px rgb(221 227 240 / 70%);
}
.waiting-list-modal-header {
text-align: end;
}
.modal-form-title {
max-width: 380px;
text-align: left;
}
#nameInput7,
#emailInput7,
#nameInput8,
#emailInput8
{
width: 100%;
margin: 6px 0;
}
.modal-form-container {
text-align: left;
margin: auto 0;
}
.first-part-modal {
display: flex;
justify-content: space-between;
}
.form-modal {
padding: 26px 0 0;
}
.bonusprogram-modal {
max-width: 730px;
padding: 1.5rem;
text-align: left;
background: #F2F4F8;
border-radius: 20px;
font-size: 16px;
line-height: 20px;
align-items: center;
letter-spacing: -0.3px;
color: #009AAE;
margin-top: 1.5rem;
}
#media (max-width: 800px) {
.waiting-list-modal-box {
width:400px;
}
.first-part-modal {
flex-direction: column;
}
.modal-form-title {
text-align: center;
padding: 1.5rem 0 0 0;
}
}
/* CSS for modals ends here */
#media only screen and (min-width: 652px) {
:root {
--responsive--aligndefault-width: min(calc(100vw - 2 * var(--global--spacing-horizontal)), 700px);
}
}
/* Blog Page styles ends here */
/* CSS for mobile screens */
/* CSS for mobile screens ends here */
/* CSS for modals starts here */
#waitingList, #waitingListEn {
color: #009AAE;
}
.modal {
position: fixed;
left: 0;
top: 0;
width: 100%;
height: 100%;
background-color: #7384a7;
opacity: 0;
visibility: hidden;
z-index: 999999999;
max-width: 100% !important;
margin: 0;
}
.modal-box {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
background-color: #ffffff;
padding: 1.5rem 2rem 3rem;
width: 60%;
border-radius: 20px;
box-shadow: 2px 2px 10px rgba(221, 227, 240, 0.7);
}
.close-button {
width: 1rem;
height: 1rem;
text-align: center;
cursor: pointer;
border-radius: 0.25rem;
color: #7384a7;
}
.show-modal {
opacity: 1;
visibility: visible;
transform: scale(1);
overflow-y: scroll;
background-color: rgba(115, 132, 167, 0.9);
}
.modal-header {
height: 40px;
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: baseline;
font-size: 30px;
}
.modal-header h1 {
font-style: normal;
font-weight: bold;
font-size: 30px;
letter-spacing: 0.3px;
color: #002832;
}
.modal-content {
border-top: 1px solid #7e8fb0;
padding-top: 1.5rem;
}
.modal-graph-exp {
font-size: 16px;
line-height: 20px;
letter-spacing: -0.3px;
color: #002832;
padding: 1rem 1rem 2rem 1rem;
}
.modal-graph-warn {
font-size: 16px;
line-height: 20px;
letter-spacing: -0.3px;
color: #002832;
background: #F2F4F8;
border-radius: 10px;
padding: 1rem;
}
.modal-content .content-with-two-section {
display: flex;
flex-direction: row;
justify-content: space-between;
padding: 0 !important;
}
.modal-content .content-with-two-section .left-content-with-image {
width: 35%;
display: flex;
align-items: center;
justify-content: center;
}
.modal-content .content-with-two-section .right-content-with-text {
width: 60%;
font-style: normal;
font-weight: normal;
font-size: 16px;
line-height: 22px;
letter-spacing: 0.3px;
color: #002832;
}
.modal-content .content-with-two-section p {
margin-bottom: 1rem;
}
.modal-content .content-with-two-section ul {
padding-left: 1.5rem;
margin-bottom: 1rem;
}
.modal-content .content-with-two-section ul li {
list-style: disc;
}
#media (max-width: 502px) {
<a id="waitingList">modal</a>
<div class="modal" id="waitingListModal">
<div class="waiting-list-modal-box">
<div class="waiting-list-modal-header">
<img src="x.svg" id="waitingListCloseButton" alt="close">
</div>
</div>
</div>
thank you very much.
You could use the History API for this, i.e. the window.onpopstate event handler in combination with window.history.pushState():
function showDialog() {
window.history.pushState({ isPopup: true }, 'Some Title');
// Place code here / add CSS class
}
function hideDialog() {
// Place code here / remove CSS class
}
// Call this function in order to close the dialog via the button -
// it will invoke the "onpopstate" event handler
function closeDialog() {
window.history.back();
}
window.addEventListener('popstate', event => {
if (event.state?.isPopup) {
hideDialog();
}
});
I'm creating a chat application(React). I'm having this textbox which I need to expand vertically upwards as user types. I tried doing it with position absolute but it takes it out of the normal flow. And this does not allow the parent div to move upwards. Is there a way that this can be done? If anyone could point me in the right direction that would be great. Thanks.
Here is the codepen link.
https://codepen.io/ghewadesumit/pen/zYzRjyY
<div class='chat-inputbox-container'>
<div class='chat-inputbox-wrapper'>
<div class='chat-microphone-container'>
<div class='chat-microphone'></div>
</div>
<div class='inputbox-container'>
<div class='inputbox-wrapper'>
<textarea type='text' class='chat-input-box'></textarea>
<div class='input-box-btn'></div>
</div>
<div class='input-character-container'>
<span class='input-character'>
250 out of 250 characters left
</span>
</div>
</div>
</div>
</div>
css
.chat-inputbox-container {
/* border: 1px solid red; */
width: 100%;
height: 113px;
align-self: flex-end;
display: flex;
justify-content: center;
}
.chat-inputbox-wrapper {
width: 736px;
height: 92px;
display: flex;
flex-direction: row;
justify-content: space-between;
}
.chat-microphone-container {
width: 32px;
height: 92px;
/* border: 1px solid orange; */
display: flex;
align-items: center;
justify-content: center;
margin-right: 10px;
}
.chat-microphone {
width: 32px;
height: 32px;
background: orangered;
}
.inputbox-wrapper {
width: 694px;
height: 52px;
/* border: 1px solid blue; */
display: flex;
align-self: flex-end;
align-items: center;
margin-top: 16px;
background: gray;
border: 1px solid red;
box-sizing: border-box;
border-radius: 4px;
}
.chat-input-box {
width: 632px;
height: 20px;
font-size: 14px;
line-height: 20px;
border: hidden;
background: #f7f7f7;
outline: none;
color: #0d1c3d;
margin: 16px 10px;
}
.input-box-btn {
/* Auto Layout */
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
padding: 0px;
position: static;
width: 32px;
height: 32px;
margin-right: 10px;
background: #0078b3;
border-radius: 4px;
flex: none;
order: 0;
flex-grow: 0;
margin: 0px 0px;
}
.input-character-container {
}
.input-character {
font-size: 12px;
line-height: 16px;
color: #677083;
}
We can use a Javascript ResizeObserver to tell us when the height of the input text has changed. Then we can scroll the window in such a way that the bottom of the input stays in the same position on the viewport - so the elements seem to grow upwards.
Note this snippet uses contenteditable on a div rather than a textarea as the growing/shrinking and text wrapping then happen automatically.
<style>
body {
height: 300vh;
}
.parent {
margin-top: 20vh; /* so we can see it growing not just disappearing */
background: pink;
}
.child {
background: #eeeeee;
}
</style>
<body>
<div class="parent">
Some stuff in the parent<br> here
<div class="child" contenteditable></div>
</div>
<script>
let prevH = 0;
const textArea = document.querySelector('.child');
const resizeObserver = new ResizeObserver(entries => {
for (let entry of entries) {
let h = entry.contentRect.height;
let diff = h - prevH;
if (diff != 0) {
prevH = h;
window.scroll(0, prevH);
}
}
});
resizeObserver.observe(textArea);
</script>
</body>
Don't hard code the height of class .inputbox-wrapper instead remove height property from there or put height: auto(which is default value) so that container expand when the the text-area is expanded
.chat-inputbox-container {
/* border: 1px solid red; */
width: 100%;
height: 113px;
align-self: flex-end;
display: flex;
justify-content: center;
}
.chat-inputbox-wrapper {
width: 736px;
height: 92px;
display: flex;
flex-direction: row;
justify-content: space-between;
}
.chat-microphone-container {
width: 32px;
height: 92px;
/* border: 1px solid orange; */
display: flex;
align-items: center;
justify-content: center;
margin-right: 10px;
}
.chat-microphone {
width: 32px;
height: 32px;
background: orangered;
}
.inputbox-wrapper {
width: 694px;
height: auto;/*Change here*/
/* border: 1px solid blue; */
display: flex;
align-self: flex-end;
align-items: center;
margin-top: 16px;
background: gray;
border: 1px solid red;
box-sizing: border-box;
border-radius: 4px;
}
.chat-input-box {
width: 632px;
height: 20px;
font-size: 14px;
line-height: 20px;
border: hidden;
background: #f7f7f7;
outline: none;
color: #0d1c3d;
margin: 16px 10px;
}
.input-box-btn {
/* Auto Layout */
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
padding: 0px;
position: static;
width: 32px;
height: 32px;
margin-right: 10px;
background: #0078b3;
border-radius: 4px;
flex: none;
order: 0;
flex-grow: 0;
margin: 0px 0px;
}
.input-character-container {
}
.input-character {
font-size: 12px;
line-height: 16px;
color: #677083;
}
<div class='chat-inputbox-container'>
<div class='chat-inputbox-wrapper'>
<div class='chat-microphone-container'>
<div class='chat-microphone'></div>
</div>
<div class='inputbox-container'>
<div class='inputbox-wrapper'>
<textarea type='text' class='chat-input-box'></textarea>
<div class='input-box-btn'></div>
</div>
<div class='input-character-container'>
<span class='input-character'>
250 out of 250 characters left
</span>
</div>
</div>
</div>
</div>
The .inputbox-wrapper should have a min-height not a height if you set only a height, the container will not resize with the textbox.
On another note, if you want the textarea to expand vertically only, you can add resize: vertical; to .chat-input-box {
Textboxes can't expand their height automatically like this by default - you'll need to use a bit of Javascript.
One approach is to dynamically calculate the height of the textfield based on the number of line breaks inside it.
This example from CSS-Tricks has more details on the approach. I've implemented it on your code below.
You also need to change height to min-height on your inputbox-wrapper to allow it to expand as the textfield changes height.
let textarea = document.querySelector(".chat-input-box");
textarea.addEventListener("keyup", () => {
textarea.style.height = calcHeight(textarea.value) + "px";
});
function calcHeight(value) {
let numberOfLineBreaks = (value.match(/\n/g) || []).length;
// min-height + lines x line-height + padding + border
let newHeight = 20 + numberOfLineBreaks * 20 + 0 + 0;
// padding and border are both 0 here but have left in for reference
return newHeight;
}
.chat-inputbox-container {
/* border: 1px solid red; */
width: 100%;
height: 113px;
align-self: flex-end;
display: flex;
justify-content: center;
}
.chat-inputbox-wrapper {
width: 736px;
height: 92px;
display: flex;
flex-direction: row;
justify-content: space-between;
}
.chat-microphone-container {
width: 32px;
height: 92px;
/* border: 1px solid orange; */
display: flex;
align-items: center;
justify-content: center;
margin-right: 10px;
}
.chat-microphone {
width: 32px;
height: 32px;
background: orangered;
}
.inputbox-wrapper {
width: 694px;
min-height: 52px;
/* border: 1px solid blue; */
display: flex;
align-self: flex-end;
align-items: center;
margin-top: 16px;
background: gray;
border: 1px solid red;
box-sizing: border-box;
border-radius: 4px;
}
.chat-input-box {
width: 632px;
height: 20px;
font-size: 14px;
line-height: 20px;
border: hidden;
background: #f7f7f7;
outline: none;
color: #0d1c3d;
margin: 16px 10px;
}
.input-box-btn {
/* Auto Layout */
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
padding: 0px;
position: static;
width: 32px;
height: 32px;
margin-right: 10px;
background: #0078b3;
border-radius: 4px;
flex: none;
order: 0;
flex-grow: 0;
margin: 0px 0px;
}
.input-character-container {
}
.input-character {
font-size: 12px;
line-height: 16px;
color: #677083;
}
<div class='chat-inputbox-container'>
<div class='chat-inputbox-wrapper'>
<div class='chat-microphone-container'>
<div class='chat-microphone'></div>
</div>
<div class='inputbox-container'>
<div class='inputbox-wrapper'>
<textarea type='text' class='chat-input-box'></textarea>
<div class='input-box-btn'></div>
</div>
<div class='input-character-container'>
<span class='input-character'>
250 out of 250 characters left
</span>
</div>
</div>
</div>
</div>
I'm currently building a Twitter clone for my portfolio using Web Component's template feature. However, my template isn't rendering on screen when the tweet button is clicked. I'm currently getting an error 'Uncaught TypeError: Cannot set property 'innerHTML' of null at HTMLButtonElement.postTweetButton.onclick'.
I have attempted moving the 'tweetText.innerHTML = tweetBoxInput;' line around to no avail. My tweetText variable's value is correct so I'm not sure why the property cannot set. I suspect it's to do with the template not importing correctly.
JS FIDDLE: https://jsfiddle.net/manoj1234/mty68qng/13/
Help greatly appreciated. Thanks.
JS:
window.onload = () => {
const createTweetContainer = document.getElementById("createTweetContainer");
const createTweetButton = document.getElementById("createTweetButton");
const backArrow = document.getElementById("backArrow");
const tweetBox = document.getElementById("tweetBox");
let tweetBoxInput;
const pinnedTweet = document.getElementById("pinnedTweet");
const tweetContainer = document.getElementById("tweetContainer");
const tweetSentContainer = document.getElementById("tweetSentContainer");
createTweetButton.onclick = () => {
createTweetContainer.style.display = "block";
tweetBox.value = "";
}
backArrow.onclick = () => {
createTweetContainer.style.display = "none";
}
postTweetButton.onclick = () => {
var tweetText = document.getElementById("tweetText");
console.log(tweetBox.value);
tweetBoxInput = tweetBox.value;
if (tweetBoxInput == "") {
console.log("please write tweet");
} else {
createTweetContainer.style.display = "none";
tweetSentContainer.style.display = "flex";
var tweetTemplate = document.getElementById("tweet-template");
var tweetInstance = document.importNode(tweetTemplate.content, true);
tweetText.innerHTML = tweetBoxInput;
pinnedTweet.after(tweetInstance);
/* Show Tweet Sent container*/
setTimeout(() => {
tweetSentContainer.style.height = "30px";
}, 1000);
setTimeout(() => {
tweetSentContainer.style.opacity = "1";
}, 1300);
/* End of Show Tweet Sent container */
/* Hide Tweet Sent container */
setTimeout(() => {
tweetSentContainer.style.opacity = "0";
}, 5000);
setTimeout(() => {
tweetSentContainer.style.height = "0";
tweetSentContainer.style.marginBottom = "0";
}, 5300);
setTimeout(() => {
tweetSentContainer.style.display = "none";
tweetSentContainer.style.marginBottom = "12px";
}, 8000);
/*End of Hide Tweet Sent container */
}
}
}
CSS:
* {
margin: 0;
padding: 0;
transition: ease 0.2s;
box-sizing: border-box;
-webkit-user-drag: none;
appearance: none;
outline: none;
font-family: 'Roboto';
}
body {
display: flex;
justify-content: center;
align-items: center;
}
body main {
width: 480px;
/*background-color: $blueBackground;
*/
}
.globalWrap {
padding-left: 25px;
padding-right: 25px;
}
.greyText {
color: #8997a2;
font-weight: normal;
}
.bodyText {
font-size: 15px;
}
#bottomFixed {
width: 100%;
position: fixed;
bottom: 0;
z-index: 4;
display: flex;
align-items: flex-end;
flex-direction: column;
}
#bottomFixed #createTweetButton {
width: 65px;
height: 65px;
font-size: 1.2em;
display: flex;
justify-content: center;
align-items: center;
color: white;
border-radius: 360px;
background-color: #1da1f2;
margin-bottom: 12px;
margin-right: 10px;
}
#bottomFixed #navBar {
height: 50px;
width: 100%;
background-color: #15202b;
border-top: solid 1px #38444d;
}
#bottomFixed #tweetSentContainer {
height: 30px;
height: 0px;
bottom: 20;
z-index: 6;
background-color: #1da1f2;
width: 80%;
display: flex;
align-self: center;
display: none;
opacity: 0;
justify-content: space-between;
align-items: center;
padding-left: 25px;
padding-right: 25px;
margin-bottom: 12px;
border-radius: 5px;
transition: ease-in-out 0.3s;
}
#coverImgContainer {
height: 125px;
width: 100%;
}
#coverImgContainer img {
height: 100%;
width: 100%;
position: relative;
object-fit: cover;
}
#userProfile {
width: 100%;
background-color: #15202b;
color: white;
}
#userProfile #userImgContainer {
display: flex;
justify-content: space-between;
align-items: flex-start;
}
#userProfile #userImgContainer button {
margin-top: 12px;
background-color: transparent;
border: solid 1px #1da1f2;
padding-left: 12px;
padding-right: 12px;
padding-top: 6px;
padding-bottom: 6px;
border-radius: 25px;
color: #1da1f2;
font-size: 0.8em;
}
#userProfile #userImgContainer h2 {
font-size: 1em;
}
#userProfile #userImgContainer #profileImgName {
position: relative;
bottom: 25;
}
#userProfile #profilePicContainer {
height: 80px;
width: 80px;
}
#userProfile #profilePicContainer img {
width: 100%;
border-radius: 100%;
border: solid 4px #15202b;
}
#userProfile #profileNav {
display: flex;
justify-content: space-between;
margin-top: 16px;
}
#userProfile #profileNav h4 {
padding-left: 10px;
padding-right: 10px;
padding-bottom: 12px;
font-size: 16px;
color: #8997a2;
}
#userProfile #profileNav h4:nth-child(1) {
color: #1da1f2;
border-bottom: solid 2px #1da1f2;
padding-left: 25px;
padding-right: 25px;
}
#userProfile #profileNav h4:nth-child(4) {
padding-right: 25px;
}
#userProfile #userInfo p:nth-child(1) {
position: relative;
bottom: 12.5;
}
#userProfile #userInfo p:nth-child(2) {
position: relative;
bottom: 6.25;
display: flex;
}
#userProfile #userInfo p:nth-child(3) {
font-weight: bold;
margin-top: 4px;
font-size: 14px;
}
#userProfile #userInfo span {
font-weight: normal;
}
#userProfile #userInfo span:nth-child(1) {
margin-right: 6px;
}
#userProfile #userInfo svg {
width: 5%;
color: #8997a2;
fill: #8997a2;
margin-right: 5px;
display: none;
}
#timelineContainer {
background-color: #12161e;
height: 100vh;
width: 100%;
}
.tweetContainer {
display: flex;
padding-top: 12px;
padding-bottom: 12px;
padding-left: 25px;
padding-right: 25px;
width: 100%;
background-color: #15202b;
border-top: solid 1px #38444d;
transition: ease-in-out 0.3s;
}
.tweetContainer .tweetName {
color: white;
}
.tweetContainer .tweetText {
color: white;
margin-bottom: 12px;
}
.tweetContainer #tweetText {
color: white;
margin-bottom: 12px;
}
.tweetContainer .tweetImgContainer {
width: 100%;
height: 200px;
display: flex;
border-radius: 12px;
overflow: hidden;
/*Add this to main container so the Border-Radius also rounds child elements*/
border: solid 1px #38444d;
}
.tweetContainer .tweetImgContainer #col-1ImgContainer {
width: 100%;
overflow: hidden;
}
.tweetContainer .tweetImgContainer #col-1ImgContainer img {
height: 100%;
width: 100%;
object-fit: cover;
border-right: solid 5px #12161e;
}
.tweetContainer .tweetImgContainer .col-2ImgContainer {
height: 50%;
overflow: hidden;
}
.tweetContainer .tweetImgContainer .col-2ImgContainer:nth-child(1) {
border-bottom: solid 5px #12161e;
}
.tweetContainer .tweetImgContainer .col-2ImgContainer img {
transform: scale(1.5, 1.5);
height: 100%;
width: 100%;
object-fit: cover;
}
.tweetProfileImgContainer {
min-width: 55px;
min-height: 55px;
max-width: 55px;
max-height: 55px;
padding-right: 12px;
}
.tweetProfileImgContainer .tweetProfileImg {
width: 100%;
border-radius: 100%;
}
/* Create Tweet Page */
#createTweetContainer {
height: 100vh;
width: 100%;
background-color: #15202b;
position: fixed;
z-index: 5;
display: none;
}
#createTweetContainer img {
margin-right: 12px;
margin-left: 25px;
}
#createTweetContainer #createTweetHeader {
height: 45px;
border-bottom: solid 1px #38444d;
color: white;
display: flex;
justify-content: space-between;
align-items: center;
padding-left: 25px;
padding-right: 25px;
}
#createTweetContainer #createTweetHeader i {
color: #1da1f2;
}
#createTweetContainer #createTweetHeader button {
background-color: #1da1f2;
border: solid 1px #1da1f2;
padding-left: 24px;
padding-right: 24px;
padding-top: 6px;
padding-bottom: 6px;
border-radius: 25px;
color: white;
font-size: 0.8em;
font-weight: bold;
opacity: 0.5;
}
#profileTweetBoxContainer {
display: flex;
justify-content: space-between;
padding-top: 12px;
}
textarea {
margin-left: 25px;
width: 100%;
resize: none;
background-color: #15202b;
border: 0;
color: white;
outline: none;
padding-right: 25px;
font-family: Arial, Helvetica, sans-serif;
font-size: 16px;
position: relative;
top: 10;
caret-color: #1da1f2;
}
HTML:
<script src="https://kit.fontawesome.com/cd801faa65.js" crossorigin="anonymous"></script>
<div id="bottomFixed">
<div id="createTweetButton">
<i class="fas fa-feather"></i>
</div>
<div id="tweetSentContainer">
<p><i class="fas fa-check-circle"></i>Your Tweet was sent.</p>
<p>View</p>
</div>
<div id="navBar">
</div>
</div>
<section id="createTweetContainer">
<div id="createTweetHeader">
<i id="backArrow" class="fas fa-arrow-left"></i>
<button id="postTweetButton">Tweet</button>
</div>
<div id="profileTweetBoxContainer">
<figure class="tweetProfileImgContainer">
<img class="tweetProfileImg" src="https://cdn.pixabay.com/photo/2016/11/08/15/21/user-1808597_1280.png">
</figure>
<textarea id="tweetBox" cols="500" rows="10" placeholder="What's Happening?"></textarea>
</div>
</section>
<section id="timelineContainer">
<div id="pinnedTweet" class="tweetContainer">
<figure class="tweetProfileImgContainer">
<img class="tweetProfileImg" src="https://cdn.pixabay.com/photo/2016/11/08/15/21/user-1808597_1280.png">
</figure>
<div>
<h4 class="tweetName bodyText">Name <span class="greyText">#username</span></h4>
<p class="tweetText bodyText">Tweet Text Here</p>
<div class="tweetImgContainer">
<div id="col-1ImgContainer">
<img src="https://cdn.getyourguide.com/img/tour/5ac513c518061.jpeg/146.jpg">
</div>
</div>
</div>
</div>
<template id="tweet-template">
<div id="tweetContainer" class="tweetContainer">
<figure class="tweetProfileImgContainer">
<img class="tweetProfileImg" src="images/profilepicture.jpg">
</figure>
<div>
<h4 class="tweetName">Emmanuel</h4>
<p id="tweetText"></p>
</div>
</div>
</template>
</section>
I realised I was attempting to change the original template's p tag rather than the imported node. I added tweetInstance.querySelectorAll(‘p’)[0].innerHTML = tweetBoxInput; which changes the text of the new node.
I am testing my navigation bar for a project and I am using basic Html/css
and i have added Jquery so that the hover effect could affect the parent element.
$(document).ready(function () {
$(".nav-level-2").hover(
function () {
$("li>a").css("background", "white");
}
);
$(".nav-level-2").mouseleave(
function () {
$("li>a").css("background", "none");
});
});
.main-nav {
background: #000;
height: 30px;
position: relative;
overflow: visible;
z-index: 2;
width: 100%;
left: 0;
cursor: default;
}
.main-nav .inner{
height: 100%;
}
.main-nav>.inner{
text-align: justify;
}
.nav-links-container {
position: static;
/* background: red; */
height: 100%;
}
.nav-links{
padding: 0 0 0 3px;
display: inline;
margin-bottom: 20px;
overflow: hidden;
/*background-color: green; */
}
li {
vertical-align: top;
padding: 5px;
display: inline-block;
/* background: blue; */
}
li>a {
color: #FFF;
font-size: 12px;
letter-spacing: 1px;
text-transform: uppercase;
padding: 10px 9px 9px;
margin: 0 -3px;
}
li>a:hover {
background-color: white;
color:#000;
}
.nav-level-2 {
display: none;
position: absolute;
top: 30px;
left: 0;
width: 100%;
height: auto;
border-bottom: 5px solid #000;
background: red;
text-align: left;
}
.nav-level-2-container {
padding-top: 40px;
padding-bottom: 40px;
-ms-flex: 0px 1px auto;
-webkit-box-flex: 0;
-webkit-flex: 0px 1px auto;
flex: 0px 1px auto;
}
li>a:hover + .nav-level-2{
display: block;
}
.nav-level-2:hover {
display:block;
}
.row{
display: flex;
flex: 0 1 auto;
-webkit-box-orient: horizontal;
-webkit-box-direction: normal;
flex-direction: row;
}
.list-container {
padding: 0px;
}
.col-lg-2{
flex-basis: 16.666666667%;
max-width: 16.666666667%;
box-sizing: border-box;
display: flex;
flex: 0 1 auto;
-webkit-box-orient: vertical;
-webkit-box-direction: normal;
flex-direction: column;
-webkit-box-flex: 0;
background: red;
margin-left: 5px;
}
.main-nav>.inner .nav-level-2 .nav-level-2-container .heading {
text-transform: uppercase;
color: #000;
letter-spacing: 1px;
margin-bottom: 20px;
font-size: 14px;
margin: 0 0 20px;
}
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.10.1/jquery.min.js"></script>
<nav class="main-nav">
<div class="inner max-girdle-width">
<div class="nav-links-container">
<ul class="nav-links">
<li class="nav-whats-new"> <a class="nav-level-1" href="#">What's New</a>
<div class="nav-level-2">
<div class="nav-level-2-container row max-girdle-width">
<div class="list-container shop col-lg-2">
<h3 class="heading"> Shop by</h3>
</div>
</div>
</div>
</li>
</ul>
</div>
</div>
</nav>
What I am trying to achieve is when I hover over the red block,I am trying to make parent element ('What's New') to show with color #000 and background white;
SEE THIS IMAGE <--
I know that when i hover 'What's New' it does change color to white, but when I hover over redblock for navigation, the background disappears with 'What's New' disappearing with black background.
No need for JavaScript to do what you want. I think this is what you are looking for? Basically, I am using the :hover on the parent div to change the child element's background and colour.
.nav-whats-new:hover a {
background:white;
color:black;
}
Example:
.main-nav {
background: #000;
height: 30px;
position: relative;
overflow: visible;
z-index: 2;
width: 100%;
left: 0;
cursor: default;
}
.nav-whats-new:hover a {
background:white;
color:black;
}
.main-nav .inner{
height: 100%;
}
.main-nav>.inner{
text-align: justify;
}
.nav-links-container {
position: static;
/* background: red; */
height: 100%;
}
.nav-links{
padding: 0 0 0 3px;
display: inline;
margin-bottom: 20px;
overflow: hidden;
/*background-color: green; */
}
li {
vertical-align: top;
padding: 5px;
display: inline-block;
/* background: blue; */
}
li>a {
color: #FFF;
font-size: 12px;
letter-spacing: 1px;
text-transform: uppercase;
padding: 10px 9px 9px;
margin: 0 -3px;
}
li>a:hover {
background-color: white;
color:#000;
}
.nav-level-2 {
display: none;
position: absolute;
top: 30px;
left: 0;
width: 100%;
height: auto;
border-bottom: 5px solid #000;
background: red;
text-align: left;
}
.nav-level-2-container {
padding-top: 40px;
padding-bottom: 40px;
-ms-flex: 0px 1px auto;
-webkit-box-flex: 0;
-webkit-flex: 0px 1px auto;
flex: 0px 1px auto;
}
li>a:hover + .nav-level-2{
display: block;
}
.nav-level-2:hover {
display:block;
}
.row{
display: flex;
flex: 0 1 auto;
-webkit-box-orient: horizontal;
-webkit-box-direction: normal;
flex-direction: row;
}
.list-container {
padding: 0px;
}
.col-lg-2{
flex-basis: 16.666666667%;
max-width: 16.666666667%;
box-sizing: border-box;
display: flex;
flex: 0 1 auto;
-webkit-box-orient: vertical;
-webkit-box-direction: normal;
flex-direction: column;
-webkit-box-flex: 0;
background: red;
margin-left: 5px;
}
.main-nav>.inner .nav-level-2 .nav-level-2-container .heading {
text-transform: uppercase;
color: #000;
letter-spacing: 1px;
margin-bottom: 20px;
font-size: 14px;
margin: 0 0 20px;
}
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.10.1/jquery.min.js"></script>
<nav class="main-nav">
<div class="inner max-girdle-width">
<div class="nav-links-container">
<ul class="nav-links">
<li class="nav-whats-new"> <a class="nav-level-1" href="#">What's New</a>
<div class="nav-level-2">
<div class="nav-level-2-container row max-girdle-width">
<div class="list-container shop col-lg-2">
<h3 class="heading"> Shop by</h3>
</div>
</div>
</div>
</li>
</ul>
</div>
</div>
</nav>
See the edited jQuery. It's what I changed. Change background none to transparent and add the color css styles.
Is this what you wanted?
$(document).ready(function () {
$(".nav-level-2").hover(
function () {
$("li>a").css("background", "white");
$("li>a").css("color", "black");
}
);
$(".nav-level-2").mouseleave(
function () {
$("li>a").css("background", "transparent");
$("li>a").css("color", "white");
}
);
});
.main-nav {
background: #000;
height: 30px;
position: relative;
overflow: visible;
z-index: 2;
width: 100%;
left: 0;
cursor: default;
}
.main-nav .inner{
height: 100%;
}
.main-nav>.inner{
text-align: justify;
}
.nav-links-container {
position: static;
/* background: red; */
height: 100%;
}
.nav-links{
padding: 0 0 0 3px;
display: inline;
margin-bottom: 20px;
overflow: hidden;
/*background-color: green; */
}
li {
vertical-align: top;
padding: 5px;
display: inline-block;
/* background: blue; */
}
li>a {
color: #FFF;
font-size: 12px;
letter-spacing: 1px;
text-transform: uppercase;
padding: 10px 9px 9px;
margin: 0 -3px;
}
li>a:hover {
background-color: white;
color:#000;
}
.nav-level-2 {
display: none;
position: absolute;
top: 30px;
left: 0;
width: 100%;
height: auto;
border-bottom: 5px solid #000;
background: red;
text-align: left;
}
.nav-level-2-container {
padding-top: 40px;
padding-bottom: 40px;
-ms-flex: 0px 1px auto;
-webkit-box-flex: 0;
-webkit-flex: 0px 1px auto;
flex: 0px 1px auto;
}
li>a:hover + .nav-level-2{
display: block;
}
.nav-level-2:hover {
display:block;
}
.row{
display: flex;
flex: 0 1 auto;
-webkit-box-orient: horizontal;
-webkit-box-direction: normal;
flex-direction: row;
}
.list-container {
padding: 0px;
}
.col-lg-2{
flex-basis: 16.666666667%;
max-width: 16.666666667%;
box-sizing: border-box;
display: flex;
flex: 0 1 auto;
-webkit-box-orient: vertical;
-webkit-box-direction: normal;
flex-direction: column;
-webkit-box-flex: 0;
background: red;
margin-left: 5px;
}
.main-nav>.inner .nav-level-2 .nav-level-2-container .heading {
text-transform: uppercase;
color: #000;
letter-spacing: 1px;
margin-bottom: 20px;
font-size: 14px;
margin: 0 0 20px;
}
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.10.1/jquery.min.js"></script>
<nav class="main-nav">
<div class="inner max-girdle-width">
<div class="nav-links-container">
<ul class="nav-links">
<li class="nav-whats-new"> <a class="nav-level-1" href="#">What's New</a>
<div class="nav-level-2">
<div class="nav-level-2-container row max-girdle-width">
<div class="list-container shop col-lg-2">
<h3 class="heading"> Shop by</h3>
</div>
</div>
</div>
</li>
</ul>
</div>
</div>
</nav>