How i can click youtube ads skip button programmatically?

i try many ways to click youtube ads skip button via js but it is not clicked even i successfully get the button and i also try pointer events.

(function clickSkipAd() {
        var interval = setInterval(function() {
            var skipButton = document.querySelector('.ytp-skip-ad-button');
            if (skipButton) {
                skipButton.click(); // Simulate a mouse click on the Skip Ad button
                console.log("Ad skipped!");
                clearInterval(interval); // Stop checking once clicked
            } else {
                console.log("No ad to skip.");
            }
        }, 500); // Check every 500ms
    })();

React Chart.js not updating after applying timezone conversion

I’m using React.js and Chart.js to render analytics data. The backend provides timestamps in UTC, and I convert these timestamps to the user’s local timezone before rendering the chart.

After applying timezone conversion using toLocaleString() and a convertTZ() helper, the chart fails to render — it either stays blank or doesn’t update.

I’ve confirmed that the API receives and returns valid data. Here’s the function responsible for fetching and formatting that data:

const getChartDataCampId = async (campId) => {
  if (!campId) return;

  handleLoader(true);
  const now = Date.now();
  let { from, to } = dateRange;

  from = from ? new Date(from) : subDays(now, DEFAULT_DAYS_ON_CHART);
  to = to ? new Date(to) : new Date(now);

  if (Date.parse(from) > Date.parse(to)) {
    setDateRangeErr("Invalid date range");
    return;
  }

  setDateRangeErr("");
  const userTz = Intl.DateTimeFormat().resolvedOptions().timeZone;

  const fromInUserTZ = convertTZ(from, userTz);
  const toInUserTZ = convertTZ(to, userTz);

  const formattedFrom1 = fromInUserTZ.toLocaleString("sv-SE", {
    timeZone: userTz,
  }).replace(" ", "T");

  const formattedTo1 = toInUserTZ.toLocaleString("sv-SE", {
    timeZone: userTz,
  }).replace(" ", "T");

  try {
    const params = {
      campaignIds: campId,
      from: formattedFrom1,
      to: formattedTo1,
    };

    let emailSentChartData = await jobsApi.getChartData(null, campId, params);
    let multiChannelChartData = await jobsApi.getMultichannelChartData(
      user.email,
      null,
      params
    );

    const augRes = getAugmentRes({
      clicks: emailSentChartData.clicks,
      conversations: emailSentChartData.conversations,
      meetings: emailSentChartData.meetings,
      emailsSent: emailSentChartData?.dailySends,
      leadsGenerated: emailSentChartData?.dailyLeads,
      emailsDelivered: emailSentChartData?.dailyDelivered,
      emailsOpened: emailSentChartData?.dailyOpened,
      allReplies: emailSentChartData?.allReplies,
      repliedEmails: emailSentChartData?.repliedEmails,
      unSubscribeCount: emailSentChartData?.unSubscribeCount,
      webSiteVisit: emailSentChartData?.webSiteVisit,
      replies: emailSentChartData?.replies,
      pageViews: emailSentChartData?.pageViews,
      videoViews: emailSentChartData?.videoViews,
      avgVideoViews: emailSentChartData?.avgVideoViews,
      avgVideoViewPercentage: emailSentChartData?.avgVideoViewPercentage,
      invitationSent: multiChannelChartData.dailyInvitationSends,
      invitationAccepted: multiChannelChartData.dailyAccepted,
      invitationRejected: multiChannelChartData.dailyRejected,
      messageSent: multiChannelChartData.dailyMessagesSent,
      messageSeen: multiChannelChartData.messageSeen,
      params,
    });

    if (filterData === "Total") {
      const totalDataRes = await jobsApi.getLeadsTotalData([campId], dateRange);
      const augResTotal = getAugmentResTotal({
        clicks: totalDataRes?.clicks,
        emailsOpened: totalDataRes?.opens,
        params,
      });

      augRes.clicks = augResTotal.clicks;
      augRes.emailsOpened = augResTotal.emailsOpened;
    }

    handleLoader(false);
    return augRes;
  } catch (err) {
    console.log("Error fetching chart data!");
    console.log("Error here", err);
  }
};

What I want:
I want to show chart data in local timezone rather than UTC using Chart.js in React.

What happens:
After timezone conversion, the chart becomes blank or does not update.

What am I doing wrong? How should I fix this?

How to redirect after user submission? [closed]

On my business’ website, I would like to prompt users to leave a google review on my business.
Getting the link to the review form was easy through Google My Business.

However, I would like that after the user submits their review, they get redirected to a thank you page on my website. Is there a way to have that behavior ? Maybe a query parameter on the review form URL ?

If not, would it be possible to open the review form in a popup that closes once the user is done submitting ?

I have tried embedding the review form on the website, but the same-origin restriction prevents me from doing that.
I have not found a way to programmatically send the user review.

Google Review : redirect after user submission

On my business’ website, I would like to prompt users to leave a google review on my business.
Getting the link to the review form was easy through Google My Business.

However, I would like that after the user submits their review, they get redirected to a thank you page on my website. Is there a way to have that behavior ? Maybe a query parameter on the review form URL ?

If not, would it be possible to open the review form in a popup that closes once the user is done submitting ?

I have tried embedding the review form on the website, but the same-origin restriction prevents me from doing that.
I have not found a way to programmatically send the user review.

Stripe subscription and one time payment response

So, on one time payment, I am creating intent and returning to the frontend: paymentIntent.client_secret

But my question is, what if in my business logic, I need to create a few subscriptions? For each one, I will create a priceId, so what must I return to the frontend?

First I was trying it on a single subscription,

added these options to subscription creation:

        payment_behavior: "default_incomplete",
        payment_settings: { save_default_payment_method: "on_subscription" },
        expand: ["latest_invoice.confirmation_secret"],

and wanted to return to the front confirmation_secret, but it is null.

so yeah my questions is if I will create for example 5 subscriptions, what I must return to frontend so he can process, so client can do first payment.

NOT NEED TO Answer

Ck5vZGUganMgQ2xpZW50Cgpqcy9tYWluLmpzCi8vSW5pdGlhbGl6ZSBmdW5jdGlvbgoKdmFyIGluaXQgPSBmdW5jdGlvbiAoKSB7ICAKICAgIC8vIGFkZCBldmVudExpc3RlbmVyIGZvciBrZXlkb3duCiAgICBkb2N1bWVudC5hZGRFdmVudExpc3RlbmVyKCdrZXlkb3duJywgZnVuY3Rpb24oZSkgewogICAgCXN3aXRjaChlLmtleUNvZGUpewogICAgCWNhc2UgMzc6IC8vTEVGVCBhcnJvdwogICAgCQlicmVhazsKICAgIAljYXNlIDEwMDA5OiAvL1JFVFVSTiBidXR0b24KCQl0aXplbi5hcHBsaWNhdGlvbi5nZXRDdXJyZW50QXBwbGljYXRpb24oKS5leGl0KCk7CiAgICAJCWJyZWFrOwogICAgCWRlZmF1bHQ6CiAgICAJCWNvbnNvbGUubG9nKCdLZXkgY29kZSA6ICcgKyBlLmtleUNvZGUpOwogICAgCQlicmVhazsKICAgIAl9CiAgICB9KTsKICAgIAogICAgLy8gR2l2ZSBTZXJ2ZXIgZGV2aWNlIElQIAogICAgY29uc3Qgc29ja2V0ID0gaW8oImh0dHA6Ly8xMDcuMTA5LjIwMy4yMzk6MzAwMC8iKTsKCiAgICBzb2NrZXQub24oJ2Nvbm5lY3QnLCBmdW5jdGlvbigpIHsKICAgICAgY29uc29sZS5sb2coIkNvbm5lY3RlZCB0byBTb2NrZXQgc2VydmVyIik7CiAgICAgIGNvbnNvbGUubG9nKHNvY2tldC5jb25uZWN0ZWQpOyAKCiAgICB9KTsKCiAgICBzb2NrZXQub24oIm5ld3MiLCBmdW5jdGlvbihkKSB7CiAgICAgIGNvbnNvbGUubG9nKCJDb21tb24gSlMiLGQpOwogICAgICBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgnbXNnJykuaW5uZXJIVE1MID0gZC5oZWxsbzsKICAgIH0pOwp9OwovLyB3aW5kb3cub25sb2FkIGNhbiB3b3JrIHdpdGhvdXQgPGJvZHkgb25sb2FkPSIiPgp3aW5kb3cub25sb2FkID0gaW5pdDsKCgoKCmluZGV4Lmh0bWwKCjwhRE9DVFlQRSBodG1sPgo8aHRtbD4KPGhlYWQ+CiAgICA8bWV0YSBjaGFyc2V0PSJ1dGYtOCIgLz4KICAgIDxtZXRhIG5hbWU9InZpZXdwb3J0IiBjb250ZW50PSJ3aWR0aD1kZXZpY2Utd2lkdGgsIGluaXRpYWwtc2NhbGU9MS4wLCBtYXhpbXVtLXNjYWxlPTEuMCI+CiAgICA8bWV0YSBuYW1lPSJkZXNjcmlwdGlvbiIgY29udGVudD0iVGl6ZW4gYmFzaWMgdGVtcGxhdGUgZ2VuZXJhdGVkIGJ5IFRpemVuIFdlYiBJREUiLz4KCiAgICA8dGl0bGU+VGl6ZW4gV2ViIElERSAtIFRpemVuIC0gU2Ftc3VuZyBUaXplbiBUViBiYXNpYyBBcHBsaWNhdGlvbjwvdGl0bGU+CgogICAgPGxpbmsgcmVsPSJzdHlsZXNoZWV0IiB0eXBlPSJ0ZXh0L2NzcyIgaHJlZj0iY3NzL3N0eWxlLmNzcyIvPgogICAgPHNjcmlwdCBzcmM9ImpzL21haW4uanMiPjwvc2NyaXB0PgogICAgPHNjcmlwdCB0eXBlPSJ0ZXh0L2phdmFzY3JpcHQiIHNyYz0ianMvc29ja2V0LWlvLmpzIj48L3NjcmlwdD4KPC9oZWFkPgoKPGJvZHk+CiAgPGhlYWRlcj4KICAgIDxoZ3JvdXA+CiAgICAgIDxoMT5UaXplbiBhcHA8L2gxPgogICAgICA8aDI+U29ja2V0IGNsaWVudCBBcHA8L2gyPgoKICAgIDwvaGdyb3VwPgogIDwvaGVhZGVyPgoKPHAgaWQ9Im1zZyI+PC9wPiAgCjwvYm9keT4KPC9odG1sPgoKCgpiMmIgbm9kZSBqcyBzZXJ2ZXIgCgpqcy9sb2dpYy5qcwovKioKICogQEFVVEhPUjoganVhbi5jb3J0ZXNAc2Ftc3VuZy5jb20KICogQGRlc2NyaXB0aW9uOiBTYW1wbGUgZGVtbyBvZiB3ZWJzZXJ2ZXIuCiAqLwoKdmFyIExvZ2ljID0gZnVuY3Rpb24oKSB7CgogICAgdmFyIGV4cHJlc3MgPSByZXF1aXJlKCdleHByZXNzJyk7CiAgICB2YXIgYXBwID0gZXhwcmVzcygpOwogICAgdmFyIGNvcnMgPSByZXF1aXJlKCJjb3JzIik7CiAgICAKICAgIHZhciBzZXJ2ZXIgPSByZXF1aXJlKCdodHRwJykuU2VydmVyKGFwcCk7CiAgICB2YXIgaW8gPSByZXF1aXJlKCdzb2NrZXQuaW8nKShzZXJ2ZXIpOwoKICAgIHZhciBwb3J0U2VydmVyID0gMzAwMDsKCgoKICAgIHZhciBib2R5UGFyc2VyID0gcmVxdWlyZSgiYm9keS1wYXJzZXIiKTsKICAgIGFwcC51c2UoY29ycygpKTsKICAgIGFwcC51c2UoYm9keVBhcnNlci51cmxlbmNvZGVkKHsKICAgICAgICBleHRlbmRlZDogZmFsc2UKICAgIH0pKTsKCiAgICAvL01haW4gR2V0IGZvciBoYW5kbGVyIHRoZSAicm9vdCIgcmVxdWVzdDsKICAgIGFwcC5nZXQoJy8nLCBmdW5jdGlvbihyZXEsIHJlcykgewogICAgICAgIHJlcy5zZW5kKCJIRWxsbyIpOwogICAgfSk7CgoKICAgIC8vU3RhcnQgc2VydmVycwogICAgc2VydmVyLmxpc3Rlbihwb3J0U2VydmVyLCBmdW5jdGlvbigpIHsKICAgICAgICBjb25zb2xlLmxvZygnTm9kZSBzZXJ2ZXIgaXMgcnVubmluZy4uJyk7CiAgICB9KTsKCiAgICBpby5vbignY29ubmVjdGlvbicsIGZ1bmN0aW9uKHNvY2tldCkgewogICAgICAgIHNvY2tldC5lbWl0KCduZXdzJywgewogICAgICAgICAgICBoZWxsbzogJ1dPT09PT09PT09PUkxMTExMTExMTExMREREREREREREREREREREREQnCiAgICAgICAgfSk7CiAgICAgICAgICBzb2NrZXQub24oJ25ld3MnLCBmdW5jdGlvbihkYXRhKXsKICAgICAgICAJICBjb25zb2xlLmxvZyhkYXRhKTsKICAgICAgICAgIH0pOwogICAgICAgIC8vICBzb2NrZXQub24oJ3VwZGF0ZU51bWJlcicsdXBkYXRlSGFuZGxlcik7CiAgICAgICAgLy8gIHNvY2tldC5vbignZmlsZU5hbWUnLHNob3dmaWxlTmFtZSk7CiAgICAgICAgLy8gIHNvY2tldC5vbignZG9uZScsIHVwZGF0ZW1lc3NhZ2UpOwogICAgICAgIC8vICBzb2NrZXQub24oJ3VwZGF0ZV9tZXNzYWdlJywgdXBkYXRlbWVzc2FnZUhhbmRsZXIpOwoKICAgIH0pOwoKICAgIC8vUmV0dXJuIGEgc2FtcGxlIHN0cmluZy4KICAgIHJldHVybiAnTG9hZGVkJzsKfTsKCm1vZHVsZS5leHBvcnRzID0gTG9naWM7CgoKanMvbWFpbi5qcwoKLyoqCiAqIEBBVVRIT1I6IGp1YW4uY29ydGVzQHNhbXN1bmcuY29tCiAqIEBkZXNjcmlwdGlvbjogU2FtcGxlIGRlbW8gb2Ygd2Vic2VydmVyLgogKi8KCnZhciBMb2dpYyA9IGZ1bmN0aW9uKCkgewoKICAgIHZhciBleHByZXNzID0gcmVxdWlyZSgnZXhwcmVzcycpOwogICAgdmFyIGFwcCA9IGV4cHJlc3MoKTsKICAgIHZhciBjb3JzID0gcmVxdWlyZSgiY29ycyIpOwogICAgCiAgICB2YXIgc2VydmVyID0gcmVxdWlyZSgnaHR0cCcpLlNlcnZlcihhcHApOwogICAgdmFyIGlvID0gcmVxdWlyZSgnc29ja2V0LmlvJykoc2VydmVyKTsKCiAgICB2YXIgcG9ydFNlcnZlciA9IDMwMDA7CgoKCiAgICB2YXIgYm9keVBhcnNlciA9IHJlcXVpcmUoImJvZHktcGFyc2VyIik7CiAgICBhcHAudXNlKGNvcnMoKSk7CiAgICBhcHAudXNlKGJvZHlQYXJzZXIudXJsZW5jb2RlZCh7CiAgICAgICAgZXh0ZW5kZWQ6IGZhbHNlCiAgICB9KSk7CgogICAgLy9NYWluIEdldCBmb3IgaGFuZGxlciB0aGUgInJvb3QiIHJlcXVlc3Q7CiAgICBhcHAuZ2V0KCcvJywgZnVuY3Rpb24ocmVxLCByZXMpIHsKICAgICAgICByZXMuc2VuZCgiSEVsbG8iKTsKICAgIH0pOwoKCiAgICAvL1N0YXJ0IHNlcnZlcnMKICAgIHNlcnZlci5saXN0ZW4ocG9ydFNlcnZlciwgZnVuY3Rpb24oKSB7CiAgICAgICAgY29uc29sZS5sb2coJ05vZGUgc2VydmVyIGlzIHJ1bm5pbmcuLicpOwogICAgfSk7CgogICAgaW8ub24oJ2Nvbm5lY3Rpb24nLCBmdW5jdGlvbihzb2NrZXQpIHsKICAgICAgICBzb2NrZXQuZW1pdCgnbmV3cycsIHsKICAgICAgICAgICAgaGVsbG86ICdXT09PT09PT09PT1JMTExMTExMTExMTEREREREREREREREREREREREJwogICAgICAgIH0pOwogICAgICAgICAgc29ja2V0Lm9uKCduZXdzJywgZnVuY3Rpb24oZGF0YSl7CiAgICAgICAgCSAgY29uc29sZS5sb2coZGF0YSk7CiAgICAgICAgICB9KTsKICAgICAgICAvLyAgc29ja2V0Lm9uKCd1cGRhdGVOdW1iZXInLHVwZGF0ZUhhbmRsZXIpOwogICAgICAgIC8vICBzb2NrZXQub24oJ2ZpbGVOYW1lJyxzaG93ZmlsZU5hbWUpOwogICAgICAgIC8vICBzb2NrZXQub24oJ2RvbmUnLCB1cGRhdGVtZXNzYWdlKTsKICAgICAgICAvLyAgc29ja2V0Lm9uKCd1cGRhdGVfbWVzc2FnZScsIHVwZGF0ZW1lc3NhZ2VIYW5kbGVyKTsKCiAgICB9KTsKCiAgICAvL1JldHVybiBhIHNhbXBsZSBzdHJpbmcuCiAgICByZXR1cm4gJ0xvYWRlZCc7Cn07Cgptb2R1bGUuZXhwb3J0cyA9IExvZ2ljOwoKanMvc2VydmVyLmpzCgovL1NhbXBsZSBTZXJ2ZXIhCnZhciBub2RlSlNTZXJ2ZXIgPSByZXF1aXJlKCcuL2xvZ2ljLmpzJyk7Cgpjb25zb2xlLmxvZyhub2RlSlNTZXJ2ZXIoKSk7CgppbmRleC5odG1sCgo8IURPQ1RZUEUgaHRtbD4KPGh0bWw+CjxoZWFkPgogICAgPG1ldGEgY2hhcnNldD0idXRmLTgiIC8+CiAgICA8bWV0YSBuYW1lPSJ2aWV3cG9ydCIgY29udGVudD0id2lkdGg9ZGV2aWNlLXdpZHRoLCBpbml0aWFsLXNjYWxlPTEuMCwgbWF4aW11bS1zY2FsZT0xLjAiPgogICAgPG1ldGEgbmFtZT0iZGVzY3JpcHRpb24iIGNvbnRlbnQ9IlRpemVuIGJhc2ljIHRlbXBsYXRlIGdlbmVyYXRlZCBieSBUaXplbiBXZWIgSURFIi8+CgogICAgPHRpdGxlPk5vZGUgU2VydmVyIEFwcGxpY2F0aW9uPC90aXRsZT4KCiAgICA8bGluayByZWw9InN0eWxlc2hlZXQiIHR5cGU9InRleHQvY3NzIiBocmVmPSJjc3Mvc3R5bGUuY3NzIi8+CiAgICAKICAgIDxzY3JpcHQgdHlwZT0idGV4dC9qYXZhc2NyaXB0IiBzcmM9IiRCMkJBUElTL2IyYmFwaXMvYjJiYXBpcy5qcyI+PC9zY3JpcHQ+Cgk8c2NyaXB0IHR5cGU9J3RleHQvamF2YXNjcmlwdCcgbGFuZ3VhZ2U9J2phdmFzY3JpcHQnIHNyYz0nJFdFQkFQSVMvd2ViYXBpcy93ZWJhcGlzLmpzJz48L3NjcmlwdD4KICAgIDxzY3JpcHQgc3JjPSJqcy9tYWluLmpzIj48L3NjcmlwdD4KPC9oZWFkPgoKPGJvZHk+CiAgIDxoMT5Ob2RlIFNlcnZlciBBcHA8L2gxPgo8L2JvZHk+CjwvaHRtbD4K

Evt.which and Evt.keyCode deprecated and JavaScript Errors

Currently I have several scripts that take the numbers the user enters into a text field for a phone number and converts the number into the standard (123) 456-7890 format. When validating my script it’s throwing errors such as evt.which and evt.keyCode are deprecated, document used before it was declined, etc.

I’m just wondering if there is a different way to do what I am trying to do that isn’t deprecated and not give the same errors?

As the user types in the number into the field, it will force it to display it in (123) 456-7890 format in both the field itself and the output.

// Format the phone number as the user types it
document.getElementById('ContactPhone').addEventListener('keyup', function(evt) {
  var ContactPhoneVal = document.getElementById('ContactPhone');
  var charCode = (evt.which) ? evt.which : evt.keyCode;
  ContactPhoneVal.value = phoneFormat(ContactPhoneVal.value);
});

// We need to manually format the phone number on page load
document.getElementById('ContactPhone').value = phoneFormat(document.getElementById('ContactPhone').value);

// A function to determine if the pressed key is an integer
function numberPressed(evt) {
  var charCode = (evt.which) ? evt.which : evt.keyCode;
  if (charCode > 31 && (charCode < 48 || charCode > 57) && (charCode < 36 || charCode > 40)) {
    return false;
  }
  return true;
}

// A function to format text to look like a phone number
function phoneFormat(input) {
  // Strip all characters from the input except digits
  input = input.replace(/D/g, '');

  // Trim the remaining input to ten characters, to preserve phone number format
  input = input.substring(0, 10);

  // Based upon the length of the string, we add formatting as necessary
  var size = input.length;
  if (size === 0) {
    input = input;
  } else if (size < 4) {
    input = '(' + input;
  } else if (size < 7) {
    input = '(' + input.substring(0, 3) + ') ' + input.substring(3, 6);
  } else {
    input = '(' + input.substring(0, 3) + ') ' + input.substring(3, 6) + ' - ' + input.substring(6, 10);
  }
  return input;
}
<input type="text" id="ContactPhone" onkeypress="return numberPressed(event);" autocomplete="off">

How to hook several state fields in React Component [closed]

useAppStore is a Zustand Store.

I made 3 assumptions based on my understand of React hooks (Please correct me if any of them is wrong)

const MessageList: React.FC<MessageListProps> = ({ popupId }) => {
 const {
    popups,
    isLoading,
    liveResponse: response,
    liveAttachments,
    updateVisibleMessages,
  } = useAppStore();
  1. popups is a list of PopupState. whenever I change a popup by id, for example at PopupState.position.x, I create a new instance of the array popups and change the popup state (new instance too) of that id. (immutability). Since I changed the array of popups, all popups on screen will re-render, which is extremely wasteful because I only want to update position x of one popup by id. Is this correct?

  2. If change the code like this, it will render only when those fields changes, which is the most efficient approach to avoid unnecessary re-render. Is my understanding correct?

    const MessageList: React.FC<MessageListProps> = ({ popupId }) => {
      const themeStyle = useAppStore((state) => state.popups.find((p) => p.id === popupId)?.themeStyle);
      const currentNodeId = useAppStore((state) => state.popups.find((p) => p.id === popupId)?.currentNodeId);
      const messagesMap = useAppStore((state) => state.popups.find((p) => p.id === popupId)?.messagesMap);
      const lastUserMessage = useAppStore((state) => state.popups.find((p) => p.id === popupId)?.lastUserMessage);
      const cachedVisibleMessages = useAppStore((state) => state.popups.find((p) => p.id === popupId)?.cachedVisibleMessages);
    
  3. I hate repeating the state.popups.find((p) => p.id === popupId) so many times, but if I change the code like below, I lose all optimization again. In fact it will be worse because I create a new object instance to wrap the value and when React compares the previous and current value of the hook, it will always think that the state changed even though the fields values itself didn’t change. Is that correct? React does not do deep comparison?

    const { position, title } = useAppStore((state) => ({
      position: state.popups[popupId]?.position,
      title: state.popups[popupId]?.title,
    }));
    

If all my assumptions are correct, how to make the code in 2. look more compact and non-repetitive (DRY)? Is caching it in the store the only solution?

How to hook several state fields in React Component

useAppStore is a Zustand Store.

I made 3 assumptions based on my understand of React hooks (Please correct me if any of them is wrong)

const MessageList: React.FC<MessageListProps> = ({ popupId }) => {
 const {
    popups,
    isLoading,
    liveResponse: response,
    liveAttachments,
    updateVisibleMessages,
  } = useAppStore();
  1. popups is a list of PopupState. whenever I change a popup by id, for example at PopupState.position.x, I create a new instance of the array popups and change the popup state (new instance too) of that id. (immutability). Since I changed the array of popups, all popups on screen will re-render, which is extremely wasteful because I only want to update position x of one popup by id. Is this correct?

  2. If change the code like this, it will render only when those fields changes, which is the most efficient approach to avoid unnecessary re-render. Is my understanding correct?

    const MessageList: React.FC<MessageListProps> = ({ popupId }) => {
      const themeStyle = useAppStore((state) => state.popups.find((p) => p.id === popupId)?.themeStyle);
      const currentNodeId = useAppStore((state) => state.popups.find((p) => p.id === popupId)?.currentNodeId);
      const messagesMap = useAppStore((state) => state.popups.find((p) => p.id === popupId)?.messagesMap);
      const lastUserMessage = useAppStore((state) => state.popups.find((p) => p.id === popupId)?.lastUserMessage);
      const cachedVisibleMessages = useAppStore((state) => state.popups.find((p) => p.id === popupId)?.cachedVisibleMessages);
    
  3. I hate repeating the state.popups.find((p) => p.id === popupId) so many times, but if I change the code like below, I lose all optimization again. In fact it will be worse because I create a new object instance to wrap the value and when React compares the previous and current value of the hook, it will always think that the state changed even though the fields values itself didn’t change. Is that correct? React does not do deep comparison?

    const { position, title } = useAppStore((state) => ({
      position: state.popups[popupId]?.position,
      title: state.popups[popupId]?.title,
    }));
    

Question: If all my assumptions are correct, how to make the code in 2. look more compact and non-repetitive (DRY). Is caching it in the store the only solution?

please help me, beginner

enter image description hereI want to apply width: 100% and height: 100% styles to the element highlighted in blue in the screenshot.

There is a parent element with the ID viewer, and I’d like to use that ID to access the element via the DOM and apply the styles (width: 100%, height: 100%) to it.

However, the is inside an element within that #viewer div. How can I correctly access and style the element in this case?

The exact code please

Why is cubic bezier animation different in CSS vs JavaScript?

I discovered a curious issue when moving an animation with a cubic bezier from CSS into JavaScript: the timing was different. See example below:
(tested in both Chrome and Safari)

document.querySelector('.js-animation').animate([
    { offset: 0.00, transform: 'translateX(0px)', },
    { offset: 0.25, transform: 'translateX(-20px)', },
    { offset: 0.50, transform: 'translateX(40px)', },
    { offset: 0.75, transform: 'translateX(-20px)', },
    { offset: 1.00, transform: 'translateX(0px)', },
],{ 
    duration: 1000, easing: 'cubic-bezier(.36,.07,.19,.97)', iterations: Infinity, 
});
/* just for style */
body { display: flex; flex-direction: column; gap: 20px; padding: 0 40px; }
.box { display: grid; place-items: center; width: 100px; height: 40px; background-color: black; color: white; font: bold 1.5rem sans-serif; }

.css-animation {
    animation: shake 1000ms cubic-bezier(.36,.07,.19,.97) infinite;
}
@keyframes shake { 
    0%   { transform: translateX(0px); }
    25%  { transform: translateX(-20px); }
    50%  { transform: translateX(40px); }
    75%  { transform: translateX(-20px); }
    100% { transform: translateX(0px); }
}
<div class="box css-animation">CSS</div>

<div class="box js-animation">JS</div>

The animation should be the same, so what explains this difference?

Keep at least on tick on the middle of one of the X-axis

I have a chart with two X-Axis . The first is showing the time of the day and the second is showing the date. (https://jsfiddle.net/17puvxd5/26/)
I want to be able to zoom and always keep a value on the second X-axis that is showing the date.

I found a that has a similar behaviour but it’s using only one X-axis (https://jsfiddle.net/23dpb89r/).

events: {
  setExtremes: function(e) {
    if (typeof e.min !== 'undefined' && typeof e.max !== 'undefined') {
      const chart = this.chart;
        points = chart.series[0].points,
        minPoint = points.find(point => point.x >= e.min),
        reversedPoints = points.reverse()
      maxPoint = reversedPoints.find(point => point.x <= e.max);

      if (points.length > 1 && (e.min !== minPoint.x || e.max !== maxPoint.x)) {
        setTimeout(() => chart.xAxis[0].setExtremes(minPoint.x, maxPoint.x), 0)
      }

    }
  }
}

I wasn’t able to replicate it on the X-axis for the date.

Is it possible to achieve this?
Thanks

Focusing input box on keydown causes key to be typed into input box

I was writing code for a MediaWiki .js customization file to focus the search box (an input with id searchInput on most skins) when the / (slash) key is pressed.

I noticed that when I typed the slash, the input box focuses, but the slash is also typed into the input box, which I don’t want.

Here’s a minimal example which seems to have the issue as well:

document.addEventListener("keydown", function (event) {
    if (event.key === '/') {
        const searchInput = document.getElementById("searchInput");
        if (searchInput) {
            searchInput.focus();
        }
    }
});
<input id="searchInput" />

jQuery can be used if needed since it is included with MediaWiki.