Why -webkit-appearance: none is needed to style input[type=”search”]::-webkit-search-cancel-button

I have a CSS Element such as given below:

<input type="search" />

And styling above element with below CSS:

input[type="search"]::-webkit-search-cancel-button{
    -webkit-appearance: none;
    background: yellowgreen;
}

Why -webkit-appearance: none; is important to style the element like pseudo-classes I mentioned above?

What If I want to only change the color of cross icon ?

return event on second toggle

I have a burgermenu with an overlay using toggle to activate the overlay (upper right):

https://pitchlab.net/

I used JS to rotate the burgermenu on toggle… the overlay/menu slides down .but i cant get it it to rotate back to the original position on a second toggle as the overlay/menu clears

my script (what I was trying is in bold):

const bento_box_wrapper = document.querySelector('.bento_box_wrapper');
const bento_box = document.querySelectorAll('.bento_box');
bento_box_wrapper.onclick = function() {
const overlayWrapper = document.querySelector('.overlayWrapper');
overlayWrapper.classList.toggle('overlayWrapperSlideDown');
$('.bento_box_wrapper').addClass('blur');
bento_box_wrapper.style.transform = 'rotate(315deg)';
$('div#toggle').addClass('blur');
const mainNavLinksWrapper = document.querySelector('.mainNavLinksWrapper');
mainNavLinksWrapper.classList.toggle('mainNavLinksWrapperSlideUp');
**bento_box_wrapper.classList.toggle = 'rotate(45 deg)';**
}

Why is the return not excuted?

my codes

console results

Here are my React codes. I know that if you write a function with a parenthesis, it will be called immediately (in this case, it will be called when elements render, I have four ‘li’s) rather than being called as a callback when the event is triggered. But what I can’t understand is, why the console.log(‘first’) got excuted and the console.log(‘second’) in return function doesn’t? The return function should be excuted right after console.log(‘first’), and ‘second’ will be log in console, shouldn’t it? What is the principle here?

Thanks for all answers.

Storing Google Drive API Access Token in Redis Causes 401 UNAUTHENTICATED Error

I’ve built a web application that uses the Google Drive API across multiple pages. On each page, I need to make authenticated API calls to Google Drive. When I log in on a page, I obtain an access token via Google login and my API requests work correctly. However, refreshing or navigating to a different page forces a re-login every time.

To solve this, I implemented an automatic Google login on the main page and store the access token in Redis. The idea is to reuse the same token across pages so that users don’t have to log in again. The problem is: when I use the token stored in Redis for my API requests, I receive a 401 error.

Even though the token I get from logging in on each page exactly matches the one stored in Redis, API calls fail with the following error:

error:
  code: 401
  errors: [{…}]
  message: "Request had invalid authentication credentials. Expected OAuth 2 access token, login cookie or other valid authentication credential. See https://developers.google.com/identity-sign-in/web/devconsole-project."
  status: "UNAUTHENTICATED"

Token Storage (on page load):

$(document).ready(async function() {
    if ("{{.DriveCookie}}" === "") {
        iziToast.info({
            title: 'Warning!',
            message: 'Drive connection not established. Connecting...',
            position: 'topCenter'
        });
        const accessToken = await getAccessToken();

        // Post the access token to store it in Redis
        const formData = new FormData();
        formData.append("driveCookie", accessToken);
        formData.append("slug", "[USER_SLUG]"); // Censored

        fetch("/user/driveCookie", {
            method: "POST",
            body: formData
        })
        .then(response => response.json())
        .then(data => {
            iziToast.success({
                title: 'Success!',
                message: 'Drive connection established.',
                position: 'topCenter'
            });
        })
        .catch(error => {
            console.error("Error:", error);
            alert("An error occurred!");
        });
    }
});

File Upload Function (API Request):

async function loadFile() {
    const form = document.getElementById('uploadDocumentForm');
    const formData = new FormData(form);
    const fileInput = document.getElementById('documentFile');

    if (!fileInput.files.length) {
        iziToast.error({
            title: 'Error',
            message: 'Please select a file.',
            position: 'topCenter'
        });
        return;
    }

    const file = fileInput.files[0];
    let fileType = file.name.split('.').pop();
    if (fileType.includes('-')) {
        fileType = fileType.split('-')[0];
    }
    if(fileType === 'docx'){
        const accessToken = await getAccessToken();
        const accessTokenString = accessToken.toString();

        const metadata = {
            name: file.name,
            mimeType: file.type,
            parents: ['[PARENT_FOLDER_ID]'] // Censored
        };

        const formData2 = new FormData();
        formData2.append("metadata", new Blob([JSON.stringify(metadata)], { type: "application/json" }));
        formData2.append("file", file);

        try {
            const response = await fetch("https://www.googleapis.com/upload/drive/v3/files?uploadType=multipart", {
                method: "POST",
                headers: new Headers({ "Authorization": "Bearer " + accessTokenString }),
                body: formData2
            });

            const result = await response.json();
            if (response.ok) {
                formData.append('driveID', result.id);
            } else {
                console.error('Upload error:', result);
                iziToast.error({
                    title: 'Error',
                    message: 'Error uploading file.',
                    position: 'topCenter'
                });
            }
        } catch (error) {
            console.error('Error:', error);
            iziToast.error({
                title: 'Error',
                message: 'Error uploading file.',
                position: 'topCenter'
            });
            return;
        }
    }
}
async function getAccessToken() {
    return new Promise((resolve, reject) => {
        // Simplified for demonstration; returns the cached token
        resolve(cachedAccessToken);
    });
}

To check whether the access token is stored correctly in Redis, I printed both the previously logged-in access token stored in Redis and the newly obtained access token from the latest login to the console :

Previously logged-in access token stored in Redis: ya29.a0AXeO80S-***************ME8yaCgYKAfcSARASFQHGX2MiEqNw2FBDguC2VN4xZdFq0Q0175  // Censored
Access token obtained from a new login: ya29.a0AXeO80S-***************ME8yaCgYKAfcSARASFQHGX2MiEqNw2FBDguC2VN4xZdFq0Q0175  // Censored

Summary:

When I perform a Google login on each page, the access token is valid and my API calls work.
I store the access token in Redis (using the above token storage code) to avoid repeated logins.
However, when I use the Redis-stored token for a Drive API call (as shown in loadFile()), I receive a 401 error, even though the token from Redis is identical to the one obtained during login.
Any insights into why the API request fails with the stored token—even when both tokens match—and how to resolve this issue would be greatly appreciated.

I stored the access token in Redis to avoid re-login across pages. I expected the stored token to work for Drive API requests, but instead, I received a 401 UNAUTHENTICATED error, even though the token matched the one from login.

Why are the nested async/await is not waiting?

I have read a lot of docs and watch a bunch of video and I just can’t seem to get this to work. The workflow is this:

  1. get a http request
  2. request gets processed by an async function.
  3. this function needs to call two more async functions in seq order.

The issue is that the first async function gets called, but it doesn’t wait. My linter shows me the message ”await’ has no effect on the type of this expression.’ for both createvm and controlvm. I am using nodejs/express. I will just post the skeleton of the code. It has got to be something really simple, but I just can’t see it.

Entry point from route:

const add = async (req, res) => {
  // destructure the request body
  const { username, uid } = req.body;


  // create vm via proxmox api
  try {
    await createVM(username, pveName, uid);
    await controlVM("start", pveName, uid);
    return res.status(200).json({
      success: true,
    });
  } catch (e) {
    errorLog("vmCont", "add", e.message, req, "error");
    return res.status(500).json({
      success: false,
      error: e.message,
    });
  }
};

Here is the createvm function:

const createVM = async (username, pveName, uid) => {
  try {
    const myConfig = {
      url: `/api2/json/nodes/temppve/qemu/10000/clone`,
      method: "post",
      baseURL: `${process.env.PROXMOX_apiurl}`,
      headers: {
        Authorization: `PVEAPIToken=${process.env.PROXMOX_apikey}`,
        "Content-Type": "application/json",
      },
      data: {
        newid: uid,
        description: `Server for ${username}`,
        full: 1,
        name: username,
        storage: "local-lvm",
        target: pveName,
      },
      httpsAgent: new https.Agent({ rejectUnauthorized: false }),
    };

    await axios(myConfig);
    return;
  } catch (e) {
    errorLog("proxmox", "createVM", e.message, false, "error");
    throw new Error(e.message);
  }
};

The the controlvm needs to fire after the vm is created:

const controlVM = async (action, node, vmid) => {
  let myConfig = {
    method: "post",
    maxBodyLength: Infinity,
    baseURL: `${process.env.PROXMOX_apiurl}`,
    url: `/api2/json/nodes/${node}/qemu/${vmid}/status/${action}`,
    headers: {
      Authorization: `PVEAPIToken=${process.env.PROXMOX_apikey}`,
    },
    data: "",
    httpsAgent: new https.Agent({ rejectUnauthorized: false }),
  };

  try {
    await axios(myConfig);
    return;
  } catch (e) {
    errorLog("proxmox", "controlVM", e.message, false, "error");
    throw new Error(e.message);
  }
};

Javascript parameters always being treated as an object

I have a simple method along with a simple button click and I’m unable to get the string from the passed in button. For some reason, the object is always a string and I’m not sure why. This is for a MVC App using Telerik. In the below grid, n the button click you’ll see I’m trying to pass in rebind as a string, however, this is always an object. How do I get the string?

<button onclick="bindGrid('rebind');"> Apply Filters</button>

const activeStatesOptions = document.getElementById("activeStates");
const daysOnMarketOptions = document.getElementById("daysOnMarket");
const prospectStatusOptions = document.getElementById("prospectStatus");

function additionalInfo(rebind) {
    if (daysOnMarketOptions.options.length == 0)
    {
        return {
            state: "",
            dom: "",
            status: "",
            rebind: ""
        }
    }
    alert(JSON.stringify(rebind));
    alert(rebind);
    return {
        state: activeStatesOptions.value,
        dom: daysOnMarketOptions.value,
        status: prospectStatusOptions.value,
        rebind: rebind
        //prospectStatusOptions.options[prospectStatusOptions.selectedIndex].value
    }
}

Adding overlay in CSS only without modifying existing HTML and using overengineered Javascript

My goal is to achieve something like the Inspector Tool element selector. So, I should not change the layout/style. I just need to add an translucent overlay and border. I did this:

  .highlight-overlay {
        position: relative;
  }
  .highlight-overlay::after {
        content: "";
        position: absolute;
        top: 0;
        left: 0;
        right: 0;
        bottom: 0;
        background-color: rgba(255, 166, 0, 0.2);
        border: 2px solid blue;
        pointer-events: none;
   }

The above works great. But the big downside is position: relative; . It sometimes break the existing HTML layout (if the container’s original position was fixed or absolute or something else).

Another solution is to use position fixed and use Javascript to account for scrolling.

function addOverlay(targetElement) {
  const overlay = document.createElement('div');
  const rect = targetElement.getBoundingClientRect();

  overlay.style.position = 'absolute';
  overlay.style.top = `${window.scrollY + rect.top}px`;
  overlay.style.left = `${window.scrollX + rect.left}px`;
  overlay.style.width = `${rect.width}px`;
  overlay.style.height = `${rect.height}px`;
  overlay.style.backgroundColor = 'rgba(255, 166, 0, 0.2)';
  overlay.style.border = '2px solid blue';
  overlay.style.pointerEvents = 'none';
  overlay.style.zIndex = '9999';

  document.body.appendChild(overlay);

  // Update position on scroll
  window.addEventListener('scroll', () => {
    const rect = targetElement.getBoundingClientRect();
    overlay.style.top = `${window.scrollY + rect.top}px`;
    overlay.style.left = `${window.scrollX + rect.left}px`;
  });
}

Somehow, I think that’s a rather overengineered solution for something as simple as this.

enter image description here

How to stop one container from disappearing into another? When zooming in and out

Genuinely apologizing if this has already been asked before, I looked Everywhere and couldn’t find an answer to my specific question outside of guides on responsiveness that didn’t quite work when I tried them out (although that could be entirely on me.)

I have one container in another and I want the page to be viewable when zoomed in, however the container gets cut off by the other one it’s inside when you zoom in, but what I’d prefer is if it shrunk with it instead. I’ve tried max and min width but min width didn’t do anything. Here’s my html.

<!DOCTYPE html>
<html>
    <head>
        <link rel="stylesheet" type="text/css"
        href="style.css">       
    </head>
<body>
        <div id="wrapper">
            <!-- Outer tabs -->
            <div class="tab">
              <button class="tablinks active" data-target="profile">Profile</button>
              <button class="tablinks" data-target="ref">Ref Sheet</button>
            </div>
          
            <div id="main">
              <div id="content">
                <!-- Outer tab content: Profile -->
                <div id="profile" class="tabcontent active">
                  <div id="profileimage"></div>
                  <!-- Inner tab buttons -->
                  <div class="tab2" id="buttons2">
                    <button class="tablinks active" data-target="about">About</button>
                    <button class="tablinks" data-target="extra">Extra</button>
                  </div>
                  <!-- Wrap inner tab content in its own container -->
                  <div class="inner-tab-container">
                    <div id="about" class="tabcontent active">
                        <div id="aboutimg"></div>
                        <div id="aboutcontent"></div>
                        <div id="aboutbio"></div>
                    </div>
                    <div id="extra" class="tabcontent">Extra content</div>
                  </div>
                </div>
          
                <!-- Outer tab content: Ref Sheet -->
                <div id="ref" class="tabcontent">
                  <div id="references" class="flex-container">
                    <div class="box">
                      <img src="https://t3.ftcdn.net/jpg/03/45/05/92/360_F_345059232_CPieT8RIWOUk4JqBkkWkIETYAkmz2b75.jpg">
                    </div>
                    <!-- (other image boxes) -->
                  </div>
                </div>
              </div>
            </div>
          </div>
<script src="javascript.js"></script>
            </body>
</html> 

and css

@media screen and (max-width: 320px) { 
  .wrapper {
    display: flex;
  }
}

#wrapper {
  width: 100%;
  height: 100%;
}

#main {
  color: red;
  background-color: pink;
  max-width:76rem;
  min-width: 40rem;
  width: 52%;
  height: 30rem;
  margin-top: 3rem;
  margin-inline: auto;
  border: 6px solid;
  border-color: brown;
  border-radius: 15% / 38px;
  padding: 4px;
}

#content {
  color: rgb(0, 0, 0);
  background-color: beige;
  max-width:66rem;
  width: 96%;
  height: 27rem;
  position: relative;
  top: 1.4rem;
  margin-inline: auto;
  border: 4.5px solid brown;
  border-radius: 15% / 30px;
  overflow: auto;
  scrollbar-width: none;
}

#profileimage {
  background-color: pink;
  width: 16rem;
  height: 25rem;
  border: 2.5px solid brown;
  border-radius: 15% / 15px;
  position: relative;
  top: 0.4rem;
}

#references {
  color: red;
}

#about {
  position:relative;
  background-color:pink;
  left:17rem;
  bottom:22.6rem;
  width:27.4rem;
  height:20rem;

}

#aboutimg {
  position:relative;
  background-color:lightblue;
  width:10rem;
  height:12rem;
}

#aboutcontent {

}

#aboutbio {

}

.flex-container {
  display: flex;
  flex-wrap: wrap;
  margin: 0 auto;
}

.box {
  flex-grow: 1;
  flex-shrink: 1;
  flex-basis: calc(25% - 10px);
  margin: 5px;
}

img {
  width: 100%;
}

.tab {
  position: relative;
  text-align: center;
  top: 4.5rem;
}

.tab button {
  background-color: burlywood;
  padding: 4px;
  width: 10rem;
  font-size: 1.8rem;
  border: 6px solid brown;
  border-radius: 10% / 8px;
  cursor: pointer;
  color: rgb(176, 103, 66);
}

.tab2 {
  position: absolute;
  text-align: center;
  top: 0.5rem;
  left: 22rem;
}

.tab2 button {
  background-color: burlywood;
  width: 8rem;
  font-size: 18px;
  border: 3.5px solid brown;
  border-radius: 10% / 8px;
  cursor: pointer;
  color: rgb(176, 103, 66);
}

.tab button:hover {
  background-color: rgb(191, 147, 89);
  color: rgb(144, 58, 32);
  border: 6px solid brown;
  border-radius: 10% / 8px;
}

.tab button:active {
  background-color: rgb(153, 73, 51);
  color: rgb(70, 21, 6);
  border: 4px solid brown;
  border-radius: 10% / 8px;
}

.tabcontent {
  padding: 6px 12px;
  border-top: none;
  display: none;
}

.tabcontent.active {
  display: block;
}

@media screen and (max-width: 800px) { 

}

edit: I also have javascript



document.getElementById('wrapper').addEventListener('click', (e) => {
  const btn = e.target.closest('button.tablinks');
  if (!btn) return;
  
  const targetId = btn.dataset.target;
  const currentTab = document.getElementById(targetId);
  if (!currentTab) return;
  
  // Use the parent of the target tab as the container.
  const container = currentTab.parentElement;
  
  // Only toggle direct children of that container.
  container.querySelectorAll(':scope > .tabcontent').forEach(tab => {
    tab.classList.toggle('active', tab === currentTab);
  });
});

What am I doing wrong with my absolute positioning for my menu

I am working on a navigation menu in my React project using Tailwind CSS. I need the menu to be centered in the header area when it’s toggled on small screens.

However, the best solution I could find so far is using left-[15%] to position the menu, but it’s still not perfectly centered. I’ve tried using different approaches, but nothing seems to give me the result I’m looking for.

import { useState, useEffect } from "react";

export default function Header() {
  // States
  const [windowWidth, setWindowWidth] = useState(window.innerWidth);
  const [menuon, setMenuOn] = useState(false);

  // Functions
  useEffect(() => {
    function handleResize() {
      setWindowWidth(window.innerWidth);
    }

    window.addEventListener("resize", handleResize);
    return () => window.removeEventListener("resize", handleResize);
  }, []);

  function Menu() {
    setMenuOn(!menuon);
  }

  // SVGS
  const menusvg = (
    <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-6">
      <path stroke-linecap="round" stroke-linejoin="round" d="M3.75 9h16.5m-16.5 6.75h16.5" />
    </svg>
  );

  return (
    <div className="p-14">
      {/*Menu Ternary for Screen Size*/}
      {windowWidth >= 640 ? (
        <div className="flex  items-center justify-between font-poppins text-lg gap-5">
            <h1>
                **
            </h1>
            <menu className="gap-5 z-[2] flex">
                <button>More Info</button>
                <button>Donate</button>
                <button>Email Us</button>
            </menu>
          
        </div>
      ) : (
        <div className="flex text-lg items-center justify-between  gap-4 ">

            <h1>
                **
            </h1>
            <button
                className={`${menuon ? 'bg-slate-400' : null} hover:bg-slate-500 hover:bg-opacity-35 transition-all p-2 bg-opacity-35 rounded-xl`}
                onClick={Menu}>
                {menusvg}
            </button>
            

          <menu className={`gap-7 ${menuon ? 'opacity-100 pointer-events-auto translate-y-40 ' : 'pointer-events-none   opacity-0'}  whitespace-nowrap right-[22%] p-4 pr-24 pl-24 bg-white duration-500 z-10 will-change-transform  transition-all rounded-xl absolute flex flex-col`}>
            <button>More Info</button>
            <button>Donate</button>
            <button>Email Us</button>
            <hr />
          </menu>
        </div>
      )}
    </div>
  );
}

Javascript wait-until-then wrapper function

There are already a few javascript wait until questions on stackoverflow.
But none of them address a wrapper function.

Could anyone provide wait until functionality and then wrap it in a function like:

WaitUntilThen(condition,functiontocall);

for example:

WaitUntilThen(CalculationsDone,PrintCalculations());

Use CSS to create a 3D cube whose faces cannot be clicked when rotated to certain angles

I’m trying to use CSS to create a simple 3D cube that can be rotated by dragging, or select its each faces by clicking them. Here is the code.

const DEFAULT_ROTATION = [-27, -36];
let position = [0, 0];
let rotation = DEFAULT_ROTATION;
let isMoved = false;

const container = document.querySelector(".container");
const rotate = () => {
  container.style.setProperty("--rotate-x", rotation[0] + "deg");
  container.style.setProperty("--rotate-y", rotation[1] + "deg");
};
const reset = () => {
  position = [0, 0];
  rotation = DEFAULT_ROTATION;
  rotate();
};

const cube = document.querySelector(".cube");
cube.addEventListener("pointerdown", e => {
  position = [e.clientX, e.clientY];
  cube.setPointerCapture(e.pointerId);
});
cube.addEventListener("pointermove", e => {
  if (!cube.hasPointerCapture(e.pointerId)) return;
  const rx = floorMod(rotation[0], 360),
    ryDir = rx > 90 && rx < 270 ? 1 : -1;
  rotation = [
    rotation[0] - (Math.round(e.clientY) - position[1]),
    rotation[1] - ryDir * (Math.round(e.clientX) - position[0]) % 360,
  ];
  rotate();
  const newPosition = [Math.round(e.clientX), Math.round(e.clientY)];
  if (!isMoved && Math.hypot(position[0] - newPosition[0], position[1] - newPosition[1]) >= 1) isMoved = true;
  position = newPosition;
});
cube.addEventListener("pointerup", e => {
  position = [0, 0];
  cube.releasePointerCapture(e.pointerId);
  const target = document.elementFromPoint(e.pageX, e.pageY);
  const face = target?.closest(".face");
  if (e.button === 1 || e.button === 2) { if (!isMoved) reset(); } 
  else if (!isMoved && face instanceof HTMLElement) face.click();
  isMoved = false;
});

const faces = document.querySelectorAll(".face");
faces.forEach(face => face.addEventListener("click", e => {
  faces.forEach(face => face.classList.remove("selected"));
  face.classList.add("selected");
}));

function floorMod(x, y) {
  let result = x % y;
  if (result !== 0 && x < 0 !== y < 0)
    result += y;
  return result;
}
.container {
  --side-length: 200px;
  width: var(--side-length);
  height: var(--side-length);
  position: relative;
  transform: rotateX(var(--rotate-x, -27deg)) rotateY(var(--rotate-y, -36deg));
  transform-style: preserve-3d;
  user-select: none;
  transition: all cubic-bezier(0, 0, 0, 1) 250ms;
}

.face {
  width: var(--side-length);
  height: var(--side-length);
  position: absolute;
  align-content: center;
  overflow: clip;
  text-align: center;
  color: black;
  background-color: #ffffffb3;
  border: 2px solid #0000000f;
  border-radius: calc(8 / 200 * var(--side-length));
  pointer-events: auto;
  transition: none;

  &.front {
    transform: translateZ(calc(var(--side-length) / 2));
  }

  &.back {
    transform: translateZ(calc(var(--side-length) / -2)) rotateY(180deg);
  }

  &.left {
    transform: translateX(calc(var(--side-length) / -2)) rotateY(-90deg);
  }

  &.right {
    transform: translateX(calc(var(--side-length) / 2)) rotateY(90deg);
  }

  &.top {
    transform: translateY(calc(var(--side-length) / -2)) rotateX(90deg);
  }

  &.bottom {
    transform: translateY(calc(var(--side-length) / 2)) rotateX(-90deg);
  }

  &.selected {
    color: white;
    background-color: #005fb8;

    &:hover {
      background-color: #005fb8e0;
    }

    &:active {
      background-color: #005fb8c0;
    }
  }

  &:not(.selected) {
    &:hover {
      color: #005fb8;
    }

    &:active {
      color: #005fb880;
    }
  }
}

.cube {
  display: grid;
  place-items: center;
  width: 350px;
  height: 350px;
  perspective: 750px;
  cursor: grab;

  &:active {
    cursor: grabbing;
  }
}

body {
  display: grid;
  place-items: center;
  height: 100vh;
  margin: 0;
}

* {
  box-sizing: border-box;
}
<div class="cube">
  <div class="container">
    <div class="front face">Front</div>
    <div class="back face">Back</div>
    <div class="left face">Left</div>
    <div class="right face">Right</div>
    <div class="top face">Top</div>
    <div class="bottom face">Bottom</div>
  </div>
</div>

3D cube

In general, it works fine. However, if a face (including top, bottom, left, right, but excluding front and back) is facing the screen, it cannot be clicked. For example:

The top face is facing the screen

This face cannot be clicked, and only one of the random side faces can be clicked.

Even if I open DevTools to inspect the element (Ctrl + Shift + C), it still won’t be selected.

I tried the method here, it reduces the angle where it can’t be clicked. However, it still doesn’t work when rotated to exactly 90 degrees.

Only for Chromium issues, Firefox works fine.

Create social media style scroll on long Quarto website page

I have a Quarto website with a long page requiring a lot of scrolling. Websites like social media feeds or news feeds often have a “click to read more” button that the user runs into after scrolling a bit. It then loads a set of content, and you can keep scrolling until you get to the next button or the end of the page content, whichever comes first.

I am not very good with coding (hence my reliance on Quarto), but was able to mimic this behavior in the example below. However, for a page with a lot of content, it is tedious to hard-code each bit of content into the button i.e., to have to separately enter each bit of content (which {{< lipsum >}} is currently standing in for) within each <div class="content-section">{{< lipsum >}}</div>.

MY question is: can this “click to read more” button be set up to automatically show a certain length or quantity of content rather than hard-coding each content chunk into the button as with this example below? That seems more powerful and flexible than the current solution.

Thank you for your time!

---
title: "Reprex"
format: html
include-in-header:
  - text: |
      <style>
      .hidden {
        display: none;
      }
      </style>
      <script>
      document.addEventListener('DOMContentLoaded', function() {
        const button = document.getElementById('reveal-more');
        const sections = document.querySelectorAll('.content-section.hidden');
        let currentIndex = 0;

        button.addEventListener('click', function() {
          if (currentIndex < sections.length) {
            sections[currentIndex].classList.remove('hidden');
            currentIndex++;
            if (currentIndex === sections.length) {
              button.style.display = 'none';
            }
          }
        });
      });
      </script>
---

<div id="content-container">
  <div class="content-section">{{< lipsum >}}</div>
  <div class="content-section hidden">{{< lipsum >}}</div>
  <div class="content-section hidden">{{< lipsum >}}</div>
</div>
<button id="reveal-more">Show More</button>

Angular form control when disabled is updating incorrect value

I have a form modal with checboxes, input and select elements. On ticking checkbox, some input and select elements should be disabled. The dropdown has please select display value with stored value as null if disabled. However, even if values are cleared on UI and disabled, it is not patching it with empty and null values. It’s taking previous values.

The form control does get the updated value when doing patchvalue. However, if the disable method is called on the control, the updated value gets lost and it gets the incorrect one.

Thanks for ur help

private disableControls() { 
 const isDiscountAPercentage = this.dynamicFormMenuForm.get('calcDiscountPercentage');
 const discountPercentageInput = this.dynamicFormMenuForm.get('calcDiscountAmount'); 
 const totalBillAmount = this.dynamicFormMenuForm.get('calcTotalBillAmount'); 
 const specialOfferIncluded = this.dynamicFormMenuForm.get('specialOfferIncluded');
 const extraCharge = this.dynamicFormMenuForm.get('extraCharge');
            
 if (isDiscountAPercentage?.value == false) {                    
  specialOfferIncluded?.enable();                    
 discountPercentageInput?.disable();                     
 totalBillAmount?.disable();

if (specialOfferIncluded?.value == false) {                          
    extraCharge?.disable();                      
   }                
 }              
else {                   
specialOfferIncluded?.reset(false);                      
specialOfferIncluded?.disable();                     
extraCharge?.disable();                  
} 
             
 isDiscountAPercentage? 
  valueChanges.pipe(                     
  distinctUntilChanged()                 
          ).subscribe(newValue => {                      
if (newValue === true) {                         
 discountPercentageInput?.enable();                            
 specialOfferIncluded?.
 patchValue(false);                          
 specialOfferIncluded?.disable();                        
 extraCharge?.setValue('');                          
 extraCharge?.disable();                         
 totalBillAmount?.enable();                      
} else {                         
discountPercentageInput?.
setValue('');                        
discountPercentageInput?.
disable();                       
specialOfferIncluded?.
patchValue(false);                       
specialOfferIncluded?.enable();                          
extraCharge?.setValue('');                       
extraCharge?.disable();                          
totalBillAmount?.setValue('');                       
totalBillAmount?.disable();                      
 }          
}); 

// Subscribe specialOfferIncluded 
changes
              
specialOfferIncluded?.
valueChanges.pipe(                   
 distinctUntilChanged()                  
 ).subscribe(newValue => {                   
 if (newValue === true) {                        
  extraCharge?.enable();                         
 
isDiscountAPercentage?.
patchValue(false, { emitEvent: 
false });
} else {
                         
 extraCharge?.setValue('', { 
 emitEvent: false });
                         
extraCharge?.disable();
                         
isDiscountAPercentage?.enable();
                    }
                });
            }               

Why Does React DevTools Show the key for but Not for Elements Inside a Mapped List?

In my React component, I have two mapped lists, each with key assigned:

  1. The outer list maps over recipes, using <Fragment key={recipe.id}>

  2. The inner list maps over ingredients, using <li key={ingredient}>{ingredient}</li>

However, when inspecting the component using React DevTools, I can only see the key for the <Fragment> (recipe ID), but not for the <li> elements (ingredient keys).

According to the React documentation documentation:

JSX elements directly inside a map() call always need keys!

In my case, both Fragment‘s and li‘s key are placed inside their respective map().

Why does React DevTools display the key for <Fragment> but not for <li>? Is the key for <li> still being used internally by React? Or I simply misplaced it?

my code
dev tool

I tired to move the <li> inside <Fragment>, the DevTool simply ignored the <li> altogether.
my code 2
dev tool2

Repo