Issue with Animated Counter Functionality on Mobile Devices

I’m encountering an issue with an animated counter on my website. The counter is intended to simulate a statistic and works perfectly on desktop browsers. However, when accessed on mobile devices in a production environment, there seems to be a problem with parsing or conversion that resets any number above 999, consequently resetting the initial number.

Here’s the relevant HTML code snippet:

<span id="counter">4898988</span><br />

Here’s the relevant JavaScript code snippet:

document.addEventListener('DOMContentLoaded', (event) => {
  const counterElement = document.getElementById('counter');
  console.log('counterElement:', counterElement); // log the counterElement
  counterElement.textContent = `${parseInt(counterElement.textContent, 10).toLocaleString()} USD`; // format the initial number
  console.log('counterElement.textContent after formatting:', counterElement.textContent); // log the textContent after formatting
  animateCount(counterElement);
  
  // Animate the counter
  function animateCount(targetElement) {
    console.log('targetElement in animateCount:', targetElement); // log the targetElement in animateCount
    let count = parseInt(targetElement.textContent.replace(/,/g, ''), 10);
    console.log('count after parsing:', count); // log the count after parsing
    const finalCount = count + Math.floor(Math.random() * 31); // generate a random number between 0 and 30
    const interval = setInterval(() => {
      if (count < finalCount) {
        count++;
        targetElement.textContent = `${count.toLocaleString()} USD`;
      } else {
        clearInterval(interval);
        setTimeout(() => animateCount(targetElement), Math.random() * 9000 + 5000); // wait for a random period between 5 and 14 seconds before starting the next cycle
      }
    }, 100);
  }
});

Console log:

[Log] counterElement:
<span id="counter">4 898 988 USD</span>
[Log] after formatting: – "4 898 988 USD"
[Log] animateCount: 
<span id="counter">4 898 988 USD</span>
[Log] count after parsing: – 4 

As you can see, the ‘let count =’ line seems to not parse correctly a number above 999.

How to include external css in Svelte Custom Element?

I’m new to Svelte and I’m trying to create a custom element that relies on font-awesome fonts. However, when I build the component and try to use it — none of my styling (including the font-awesome css) gets applied.

I really don’t want the user of my element to have to include font-awesome on their own — I want it to just be included as part of my custom element.

Just curious what I need to do to get this to work?

Here’s what I have:

App.js

<svelte:options customElement="my-custom-element"></svelte:options> 

<script>
   export let overrideCss = null;  //Give user of element chance to override css
   ...
</script>

<!-- Css below is applied when running dev server, but doesn't get applied after build -->
<link rel="stylesheet" href="/src/app.css">
<link href="./../node_modules/@fortawesome/fontawesome-free/css/all.min.css" rel="stylesheet"> 
{#if overrideCss}
    <link rel="stylesheet" href={overrideCss}/>
{/if}

<div id="my-app-container">
  ...
  <i class="fas fa-thumbs-{approved ? 'up' : 'down'}"></i>
</div>

Vanilla JavaScript to show scroll progress bar that fills only while scrolling the element?

I’m trying to create a scroll progress bar that fills from 0-100% when scrolling the element only, but i can’t get it to work. I’ve got it working to show the whole page scrolling progress when scrolling the element, but I need it to only show 0-100% on the element, and only while that element is being scrolled. I also need to create it using vanilla javascript.

Here’s what i have that isn’t working:

HTML

<body>
    <header>
        <section>
        </section>
    </header>
    <nav>
        <div class="nav"></div>
        <div class="scroll">
                <div class="scroll-bar" id="scroll-progress"></div>
        </div>
    </nav>
    <main>
        <section>
            <p>some content</p>
        </section>
        <section>
            <p>some content</p>
        </section>
    </main>
    <section>
        <p>some content</p>
    </section>
    <section>
        <p>some content</p>
    </section>
    
</body>

CSS

.nav {
    background-color: black;
    height:50px;
}
section {
    height: 400px;
}

.scroll {
    width: 100%;
    height: 4px;
    background-color: lightgray;
}

#scroll-progress {
    width: 0%;
    height: 4px;
    background-color: green;
}

JS

const main = document.querySelector('main');
const progress = () => {
    const scroll = main.scrollTop;
    const height = main.scrollHeight - main.clientHeight;
    const scrollProgress = (scroll / height) * 100;
    document.getElementById('scroll-progress').style.width = scrollProgress + "%";
}

window.addEventListener('scroll', progress);

This, however, works as expected, but shows the progress through the whole page, while scrolling the element, I’m just not sure how to get from this to what i need.

const main = document.querySelector('main');
const progress = () => {
    let scroll = document.documentElement.scrollTop;
    let height = document.documentElement.scrollHeight - document.documentElement.clientHeight;
    let scrollProgress = (scroll / height) * 100;
    document.getElementById("scroll-progress").style.width = scrollProgress + "%";
}

window.addEventListener('scroll', progress);

Any help greatly appreciated

How do I fix the following issues with my port of a manifest v2 browser extension to manifest v3?

I am in the process of transitioning a manifest v2 Chrome extension to manifest v3 and while converting my background script background.js to a service worker, and I am getting error messages in Google Chrome. I am somewhat familiar with Manifest v3, however the concept of service workers is still very confusing to me. Here is the error message. Note: Keep in mind, despite the single error message, I believe there may be some logical errors rather than syntax errors in my source code that may present undesired or unexpected behavior in the browser extension.

Uncaught (in promise) Error: Could not establish connection. Receiving end does not exist.

To put this error into context, I have included the code of my service worker called service-worker.js, content-script.js, and manifest.json. I think I did most of the “heavy lifting” porting my MV2 extension to MV3, but there seems to be some minor issues.

service-worker.js

"use strict";

function sendAudioMessage(actionType, audioFile) {
  chrome.tabs.query({ active: true, currentWindow: true }, function(tabs) {
    tabs.forEach(tab => {
      chrome.tabs.sendMessage(tab.id, { action: actionType });
    });
  });
}

chrome.webNavigation.onCreatedNavigationTarget.addListener(onNav);
chrome.webNavigation.onBeforeNavigate.addListener(onNav);
chrome.webNavigation.onReferenceFragmentUpdated.addListener(onNav);
chrome.webNavigation.onHistoryStateUpdated.addListener(onNav);

function onNav({frameId}) {
  if(frameId > 0) return;
  sendAudioMessage('playNavigationSound');
}

chrome.downloads.onChanged.addListener(delta => {
  if(delta.state && delta.state.current === "complete") {
    sendAudioMessage('playDownloadCompleteSound');
  }
  if(delta.error && delta.error.current) {
    sendAudioMessage('playDownloadErrorSound');
  }
});

chrome.tabs.onUpdated.addListener((tabId, {mutedInfo}) => {
  if(mutedInfo && mutedInfo.reason === "user") {
    sendAudioMessage('playMuteWarningSound'); 
  }
});

function changeVolume(volumeLevel) {
  chrome.tabs.query({ active: true, currentWindow: true }, function(tabs) {
    tabs.forEach(tab => {
      chrome.tabs.sendMessage(tab.id, { action: 'changeVolume', volume: volumeLevel });
    });
  });
}

// *** Message Handler ***
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
  switch (message.action) {
    case 'playNavigationSound':
      sendAudioMessage('playNavigationSound');
      break;
    case 'playDownloadCompleteSound':
      sendAudioMessage('playDownloadCompleteSound');
      break;
    case 'playDownloadErrorSound':
      sendAudioMessage('playDownloadErrorSound');
      break;
    case 'playMuteWarningSound':
      sendAudioMessage('playMuteWarningSound');
      break;
    case 'changeVolume':
      changeVolume(0.75); 
      break;
    default:
      console.log('Unknown message action:', message.action);
  }
});

content-script.js

// Helper function to play audio
function playAudio(audioFile) {
  const audio = new Audio(chrome.runtime.getURL(audioFile)); // Use chrome.runtime.getURL for packaged extension resources
  audio.play();
}

// Helper function for setting volume
function setAudioVolume(volumeLevel) {
  const audioElements = document.querySelectorAll('audio');
  audioElements.forEach(audio => {
      audio.volume = volumeLevel;
  });
}

// Receive messages from your service worker
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
  switch (message.action) {
      case 'playNavigationSound':
          playAudio('nav.ogg');
          break;
      case 'playDownloadCompleteSound':
          playAudio('done.ogg');
          break;
      case 'playDownloadErrorSound':
          playAudio('error.ogg');
          break;
      case 'playMuteWarningSound':
          playAudio('unlock.ogg');
          break;
      case 'changeVolme':
          setAudioVolume(0.75);
          break;
      default:
          console.log('Unknown message action:', message.action);
  }
});
  

manifest.json

{
  "manifest_version": 3,
  "name": "PopupSound",
  "description": "Plays a click sound when you click on a link. Also plays a trumpet sound when you finish downloading a file.",
  "author": "Michael Gunter",
  "version": "3.0",
  "default_locale": "en",
  "offline_enabled": true,
  "icons": {
    "32": "icon_32.png",
    "96": "icon_96.png",
    "128": "icon_128.png"
  },
  "background": {
    "service_worker": "service-worker.js"
  },
  "permissions": [
    "webNavigation",
    "downloads",
    "tabs"
  ],

  "content_scripts": [
    {
      "matches": ["<all_urls>"], 
      "js": ["content-script.js"],
      "run_at": "document_start"
    }
  ],

  "content_security_policy": {}
}

Edit: While I no longer get the error message when the extension runs, I now get two error messages when audio is expected to be played from the browser extension.

Uncaught (in promise) Error: Could not establish connection. Receiving end does not exist.

Uncaught (in promise) NotAllowedError: play() can only be initiated by a user gesture.

I hope this helps.

How to Update Input Value without onChange in React?

I’ve been working on a React component where I have implemented handleStepUp and handleStepDown functions for incrementing and decrementing values. However, I’m facing a challenge with updating the input value within these functions since they don’t have access to an event like onChange. The onChange event is crucial for updating the input value in React, but I can’t directly use it within handleStepUp or handleStepDown.

import * as React from 'react';

import { Input } from '@/components/shadcnui/input';

export interface InputProps
  extends React.InputHTMLAttributes<HTMLInputElement> {
  onChange?: (event: ChangeEventExtendedProps) => void;
  defaultValue?: string;
  step?: number;
  onStepUp?: () => void; // Menambah properti onStepUp
  onStepDown?: () => void; // Menambah properti onStepDown
}

export interface CustomChangeEvent {
  target: { floatValue: number; formattedValue: string; value: string };
}
export interface ChangeEventExtendedProps
  extends React.ChangeEvent<HTMLInputElement> {
  target: HTMLInputElement & {
    floatValue: number | undefined;
    formattedValue: string | undefined;
  };
}

function formatNumber(number: number): string {
  const converted = number.toLocaleString(); // returns a number as a string, using local language format. 1000 => 1,000
  return converted;
}

export const NumberInputFormat = React.forwardRef<HTMLInputElement, InputProps>(
  (props, ref) => {
    const {
      onChange,
      defaultValue = 0,
      step = 1,
      onStepUp,
      onStepDown,
      min,
      max = 10,
    } = props;

    const [formattedValue, setFormattedValue] = React.useState<string>(
      defaultValue.toString()
    );

    // ketika kita mempassing sebuah fungsi ke dalam sebuah props,
    // ada baiknya kita buatkan dia useCallback sehingga fungsi
    // itu tidak dibuat kembali ketika proses re-render
    const handleChange = React.useCallback(
      (event: ChangeEventExtendedProps) => {
        const stringValue = event.target.value;

        const numericValue = stringValue
          ? parseFloat(stringValue.replace(/,/g, ''))
          : 0;

        const formattedStringValue = formatNumber(numericValue);
        setFormattedValue(formattedStringValue);
        onChange?.({
          ...event,
          target: {
            ...event.target,
            floatValue: numericValue,
            formattedValue: formattedStringValue,
            value: stringValue,
          },
        });
      },
      [onChange]
    );

    const handleStepUp = () => {
      const stringValue = formattedValue.replace(/,/g, '');
      const floatValue = parseFloat(stringValue) + step;
      const formattedStringValue = formatNumber(floatValue);
      setFormattedValue(formattedStringValue);

      onStepUp?.(); // Memanggil onStepUp jika diberikan
    };

    const handleStepDown = () => {
      const stringValue = formattedValue.replace(/,/g, '');
      const floatValue = parseFloat(stringValue) - step;
      const formattedStringValue = formatNumber(floatValue);
      setFormattedValue(formattedStringValue);

      onStepDown?.(); // Memanggil onStepUp jika diberikan
    };

    return (
      <>
        <button type="button" onClick={handleStepUp}>
          +
        </button>
        <button type="button" onClick={handleStepDown}>
          -
        </button>
        <Input
          {...props}
          value={formattedValue}
          onChange={handleChange}
          ref={ref}
          type="text"
        />
      </>
    );
  }
);

NumberInputFormat.displayName = 'NumberInputFormat';

What would be the best approach to update the input value without relying on the onChange event? Is there a way to achieve this while maintaining the React best practices? Any insights or alternative methods would be greatly appreciated. Thank you!

window.open can’t attach params to a url ends with .html

while I’m using

window.open(‘/a.html?title=666’)

there is no title = 666 in the new window’s url

but

window.open(‘/a?title=666’)

works fine.

it’s really confused me.

by the way, I’m using node’s ‘serve’ npm package to test in localhost.

during the research of documents, it is supposed to be the same.

How to add spacing between Legend and Chart in Recharts with absolute positioning?

I’m using Recharts for the first time in my React project
I’ve positioned the legend of my BarChart component to the top right corner.
However, I’m struggling to add spacing between the legend and the chart itself. Since the legend is positioned absolutely, I can’t set any margin directly on it.

When attempting to change the legend’s style to relative positioning, the legend gets displayed under the chart, ignoring the align and verticalAlign properties

Here’s a snippet of my code:

<BarChart data={data}
          barGap={8}>
    <CartesianGrid strokeDasharray="3 3" vertical={false}/>
    <XAxis tickFormatter={(index) => index + 1} tickMargin={16} tickLine={false}/>
    <YAxis orientation={"right"} tickMargin={16} axisLine={false} tickLine={false}/>
    <Tooltip content={<CustomTooltip/>}/>
    <Legend
        align="right" verticalAlign="top"
        iconType="circle" iconSize={12}
        formatter={(value, entry) => {
            return <LegendText>{value} ({entry.payload.unit})</LegendText>
        }}/>
    <Bar name="Poids" unit="kg" dataKey="kilogram" fill="#282D30" barSize={7}
         radius={[10, 10, 0, 0]}/>
    <Bar name="Calories brûlées" unit="kCal" dataKey="calories" fill="#E60000" barSize={7}
         radius={[10, 10, 0, 0]}/>
</BarChart>

How can I add a specific amount of spacing (e.g., 65px) between the legend and the chart in this setup?

Thank you!

Bar chart not displaying axes and legend values correctly

I am working on a page that uses datatables to retrieve highcharts. Charts for Buttons one and three are fine. The problem I am facing is with Button two – Bar chart. For the chart, I am trying to display the values from column 2022 as Y axis and Percent column as X axis. The legend should also display Y axis columns. What am I missing? Here is my code.

// Function to initialize Highcharts charts
function initializeChart(chartId, chartTitle, chartData, chartType) {
    Highcharts.chart(chartId, {
        chart: {
            type: chartType,
            styledMode: false
        },
        title: {
            text: chartTitle
        },
        colors: ['#1a4480', '#e52207', '#e66f0e', '#ffbe2e', '#fee685', '#538200', '#04c585', '#97d4ea', '#009ec1', '#0076d6', '#adadad', '#8168b3', '#d72d79', '#f2938c'],
        tooltip: {
            pointFormat: '</b> {point.y:.1f}%'
        },
        plotOptions: {
            bar: {
                dataLabels: {
                    enabled: true,
                    format: '{point.y:.1f}%',
                    style: {
                        fontSize: "12px"
                    }
                },
                showInLegend: true
            },
            pie: {
                dataLabels: {
                    enabled: true,
                    format: '{point.y:.1f}%',
                    style: {
                        fontSize: "12px"
                    }
                },
                showInLegend: true
            }
        },
        legend: {
            symbolRadius: 0,
            itemStyle: {
                color: '#000000',
                fontSize: '16px'
            }
        },
        series: [{
            data: chartData
        }]
    });
}

// Function to retrieve chart data from DataTable
function getChartData(table) {
    const data = [];
    table.rows().every(function () {
        const row = this.data();
        data.push({
            name: row[0],
            y: parseFloat(row[1])
        });
    });
    return data;
}

// Check if DataTable and Highcharts are defined before calling the function
if (typeof DataTable !== 'undefined' && typeof Highcharts !== 'undefined') {
    initializeCharts();
}

function initializeCharts() {
    const table1 = new DataTable('#example1', {
        searching: false,
        info: true,
        paging: false,
        sort: false
    });
    const table2 = new DataTable('#example', {
        searching: false,
        info: true,
        paging: false,
        sort: false
    });

    const chartData1 = getChartData(table1);
    const chartData2 = getChartData(table2);

    initializeChart('demo-output', 'FY 2023', chartData1, 'pie');
    initializeChart('demo-output2', 'FY 2022', chartData2, 'bar');

    var categories = [];
    var allSeriesData = [];

    var table = $("#example2").DataTable({
        searching: false,
        responsive: true,
        lengthChange: false,
        ordering: false,
        info: false,
        paging: false,
        initComplete: function (settings, json) {
            let api = new $.fn.dataTable.Api(settings);

            var headers = api.columns().header().toArray();
            headers.forEach(function (heading, index) {
                if (index > 0 && index < headers.length) {
                    categories.push($(heading).html());
                }
            });

            let rows = api.rows().data().toArray();
            rows.forEach(function (row) {
                group = {
                    name: '',
                    data: []
                };
                row.forEach(function (cell, idx) {
                    if (idx == 0) {
                        group.name = cell;
                    } else if (idx < row.length) {
                        group.data.push(parseFloat(cell.replace(/,/g, '')));
                    }
                });
                allSeriesData.push(group);
            });
        }
    });

    Highcharts.setOptions({
        lang: {
            thousandsSep: ','
        }
    });

    var myChart = Highcharts.chart("chart-A", {
        chart: {
            type: "column",
            borderColor: 'lightgray',
            borderWidth: 1,
            marginTop: 50
        },
        tooltip: {
            headerFormat: '{point.key}<br/>',
            pointFormat: '{series.name}: <b>{point.y:.1f}%</b>'
        },
        legend: {
            symbolRadius: 0,
            itemStyle: {
                color: '#000000',
                fontSize: '16px'
            }
        },
        colors: ['#003489', '#ED7D31', '#A5A5A5', '#FFC000', '#5B9BD5'],
        credits: {
            enabled: false
        },
        title: {
            text: "Trends"
        },
        xAxis: {
            categories: categories,
            labels: {
                style: {
                    fontWeight: '600',
                    fontSize: '16px',
                    color: '#000000'
                }
            }
        },
        yAxis: {
            title: false,
            tickInterval: 10,
            max: 60,
            labels: {
                formatter: function () {
                    return Highcharts.numberFormat(this.value, 0);
                },
                style: {
                    fontWeight: '600',
                    fontSize: '16px',
                    color: '#000000'
                }
            }
        },
        series: allSeriesData
    });

    $('.usa-button').on('click', function () {
        var buttonId = $(this).attr('id');

        if (buttonId === 'previous') {
            $('#chart2').show();
            $('#chart1').hide();
            $('#chart3').hide();
        } else if (buttonId === 'trend') {
            $('#chart2').hide();
            $('#chart1').hide();
            $('#chart3').show();
        } else {
            $('#chart2').hide();
            $('#chart1').show();
            $('#chart3').hide();
        }

        $('.usa-button').removeClass('active');
        $(this).addClass('active');
    });

    $('#current').addClass('active');
}
<html>
  <head>
    <script src="https://code.jquery.com/jquery-3.7.1.min.js"></script>

    <link href="https://nightly.datatables.net/css/dataTables.dataTables.css" rel="stylesheet" type="text/css" />
    <script src="https://nightly.datatables.net/js/dataTables.js"></script>

 <script src="https://code.highcharts.com/highcharts.js"></script>

  </head>
  <body>




<div class="ar-controls grid-row tablet:flex-justify-start">
<div class="tablet:grid-col-auto margin-bottom-205"><button id="current" class="usa-button usa-button--outline" title="Select to see related information below">one</button> <button id="previous" class="usa-button usa-button--outline" title="Select to see related information below">two</button> <button id="trend" class="usa-button usa-button--outline" title="Select to see related information below">three</button></div>
</div>

<div id="chart1">
<div id="demo-output" class="chart-display" style=" margin-bottom: 2em; height: 500px; border: solid 1px lightgray;"></div>

<table id="example1" class="display" style=" width: 100%;"><thead>
<tr>
<th scope="col">2023</th>
<th scope="col">Percent</th>
</tr>

</thead>
<tr>
<td scope="row">ABC</td>
<td>45.9%</td>
</tr>

<tr>
<td scope="row">DEF</td>
<td>22.0%</td>
</tr>

<tr>
<td scope="row">GHI</td>
<td>13.6%</td>
</tr>

</table>
</div>

<div id="chart2" style=" display: none;">
<div id="demo-output2" class="chart-display2" style=" margin-bottom: 2em; height: 680px; border: solid 1px lightgray;"></div>

<table id="example" class="display" style=" width: 100%;"><thead>
<tr>
<th scope="col">2022</th>
<th scope="col">Percent</th>
</tr>

</thead>
<tr>
<td>123</td>
<td>51.90%</td>
</tr>

<tr>
<td>456</td>
<td>18.60%</td>
</tr>

</table>
</div>

<div id="chart3" style=" display: none;">
<div id="chart-A" style=" width: 100%; height: 500px;"></div>

<table class="row-border stripe no-footer cell-border padding-top-5" id="example2" style=" width: 100%;"><thead>
<tr>
<th>Year</th>
<th>column1</th>
<th>column2</th>
<th>column3</th>

</tr>

</thead>
<tr>
<td scope="row" style=" text-align: left; white-space: nowrap;">FY19</td>
<td style=" text-align: left;">42.7%</td>
<td style=" text-align: left;">17.3%</td>
<td style=" text-align: left;">9.5%</td>

</tr>

<tr>
<td scope="row" style=" text-align: left;">FY20</td>
<td style=" text-align: left;">49.5%</td>
<td style=" text-align: left;">16.3%</td>
<td style=" text-align: left;">3.4%</td>

</tr>


</table>
</div>
</div>

Chart.js is not visible, but does show up on HTML

I am creating a dashboard, on this dashboard i make a call to some charts using htmx, but the chart is not rendering properly, when i inspect the element i can see the script, canvas and code but not the visual of the graph. I am using flask and python and this is my code pre the dashboard that calls the chart:

<div>
    <div class="toolbox">
        <h1>Budget</h1>
        <div>
            <div hx-get="/auth/budget/expense_form" hx-swap="innerHTML" hx-trigger="load once" id="expense_form_field">
            </div>
            <div hx-get="/auth/budget/dashboard" hx-swap="outerHTML" id="dashboard" hx-target="#dashboard"
                hx-trigger="load once">
            </div>
        </div>
    </div> </div> </div>

And this is what html is returned:

<div>
    <canvas id="myChart"></canvas>
</div>

<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<script>
    const ctx = document.getElementById('myChart');

    const chart = new Chart(ctx, {
        type: 'bar',
        data: {
            labels: ['Red', 'Blue', 'Yellow', 'Green', 'Purple', 'Orange'],
            datasets: [{
                label: '# of Votes',
                data: [12, 19, 3, 5, 2, 3],
                borderWidth: 1
            }]
        },
        options: {
            scales: {
                y: {
                    beginAtZero: true
                }
            }
        }
    });</script>

I have looked to the following similar questions, but i have not found any of their fixes useful: enter link description here, enter link description here,enter link description here

When i check the console errors i am noticing this:

Uncaught ReferenceError: Chart is not defined
    at <anonymous>:4:19
    at At ([email protected]:1:22924)
    at Nt ([email protected]:1:23051)
    at [email protected]:1:10309
    at [email protected]:1:44741
    at oe ([email protected]:1:4604)
    at s ([email protected]:1:44716)

my thinking is that HTMX is affecting the script? Also when i inspect the chart element in browser it has an htmx class tag, could this be interfering with the script?

<script class="htmx-settling">
    const ctx = document.getElementById('myChart');

    const chart = new Chart(ctx, {
        type: 'bar',
        data: {
            labels: ['Red', 'Blue', 'Yellow', 'Green', 'Purple', 'Orange'],
            datasets: [{
                label: '# of Votes',
                data: [12, 19, 3, 5, 2, 3],
                borderWidth: 1
            }]
        },
        options: {
            scales: {
                y: {
                    beginAtZero: true
                }
            }
        }
    });</script>

How can I remove one of the clicks from hide/show?

I’m trying to show/hide more info about names on click, but don’t know how to make it work of a single click.

I’m creating a div with JS like so:

const newDiv = document.createElement("div");
newDiv.className = "game-info";
newDiv.innerHTML = `
<p onclick="toggler()";>${game.Name}</p>
<div class="info">
<img src="${game.Thumbnail}">
<p>${game.Description}</p>
</div>
`;
document.body.appendChild(newDiv);

The toggler function is written like this:

function toggler(){
    $('div.game-info').click(function(e){
        $(this).children('.info').toggle();
    });
}

It kinda works, but it takes one or more clicks to view the additional info. I know the problem is due to multiple onclick calls, but don’t know how to make it with a single click. I tried using jQuery without the toggler function, but then it opens info on all the names and not just the one which was clicked. So if someone could either tell me how to get rid of that secondary onclick or how to properly target the info which I clicked in the innerHTML section that’d be great!

How to parse String Data to JSON format or extract name value pair

I am developing a Web Page which receives below data over socket:

{
        "version":      1,
        "timestamp":    "1990-01-01T02:10:45.265Z",
        "points":       {
                        "motor_voltage": {
                                        "present_value":        0
                                },
                        "motor_current": {
                                        "present_value":        0
                                },
                        "speed_rpm":     {
                                        "present_value":        0
                                },
                        "running_hours": {
                                        "present_value":        1
                                },
                        "drive_temperature":     {
                                        "present_value":        0
                                },
                        "analog_input_2":        {
                                        "present_value":        0
                                },
                        "status_word":   {
                                        "present_value":        "600hex"
                                },
                        "digital_input": {
                                        "present_value":        64
                                },
                        "digital_output":        {
                                        "present_value":        0
                                }
                }
                                        "booldigital_output":        {
                                        "present_value":        0
                                }
}

I have tried a log how use JavaScript to parse this string and extract like below

motor_voltage: { present_value: 0 }

And all others – like we get each key value present_values in an array? If I call JSON.parse method – it throws an error that object is not in JSON format.

How can I extract the data from above string as per the values required?

PDF Editor to replace certain Elements (SAP / JavaScript)

I got the task to create an SAP UI5 Fiori application where you can upload a pdf file and where the application automatically blacks out personal information like the name or the phone number. Or if this is not working, at least black it out by yourself through an editor function. Therefore i need to be able to have access to the text from the pdf file.

I tried to solve this via base64 encoding and i was hoping that i can simply replace the base64 string of for example “John Smith” with the encoded string of “****”. But these strings are not found in the pdf or img encoded base64 string. I could extract the text from the pdf file and replace it there but the task says that the layout and design of the given pdf file should stay the same.

So since the base64 attempt doesnt seem to work i need to build my own pdf editor. The given PDF viewer of SAP lets me view the pdf but not edit it in any way. The same seems to be the case for pdf.js since there are no tutorials on how to build an actual editor with it.

The only tutorials that i could find were some short explanations about the code, but the code itselfs lies behind a $100 paywall..

So what would be the best way to solve this task and build pdf editor?

If things are unclear or need further explanation, let me know!

Updating nested cache in Apollo GraphQL

I am fetching a list of documents from the backend using this query:

gql`
  query GetUserDocuments($userId: Int!, $filters: DocumentFilter) {
    user(userId: $userId) {
      id
      documents(filters: $filters) {
        id
        name
      }
    }
  }
`;

The following object is saved in the Apollo cache

User:{"userId":123}

which has 2 nested collections defined inside

documents({"filter":{"archived": false}})
documents({"filter":{"archived": true}})

the apollo cache looks something like this

User:{"userId":123} {
  __typename: "User",
  userId: 123,
  documents({"filter":{"archived": false}}): []
  documents({"filter":{"archived": true}}): []
}

On the frontend, I would like to handle this cache so that when I archive a given document, I remove it from the collection documents({"filter":{"archived": false}}) and I want to put it in the collection documents({"filter":{"archived": true}}). Similarly, when I unarchive a document, I remove it from the archived list and add it to the unarchived list. Is it possible to do this using a cache update? Or do I have to refetch the data, which I would like to avoid?

I tried using cache.modify, however I am getting 2 collections at the same time:

cache.modify({
  id: cache.identify({ __typename: "User", userId }),
  fields: {
    documents: (documents) => {
      return documents;
    },
  },
});

Open rowContextMenu with Button click on Tabulator Table

I want to have a button at the end of my Table where i can Click and open the rowContextMenu below the Button.

But i also want it to pop up when I rightclick anywhere on the row.

I already tried a few things

var menuButtonFormatter = (cell: any) => {
var menuBtn = document.createElement('button');
          menuBtn.type = 'button';
          menuBtn.innerHTML = '<span class="material-icons" style="color: #707070">more_horiz</span>';
          menuBtn.classList.add('menu-button');
          menuBtn.addEventListener('click', (event) => {
            event.stopImmediatePropagation();
            const myEvent: any = new Event('row-contextmenu');
            myEvent.pageX = event.pageX;
            myEvent.pageY = event.pageY;
            cell.getRow().getElement().dispatchEvent(myEvent);
          });

buttonHolder.appendChild(menuBtn);
          return buttonHolder;

        }

This is the Button

And my Column looks like this:

{
        title: this.$t('actions'),
        field: 'actions',
        // formatterParams: poParams,
        formatter:menuButtonFormatter,
        headerSort: false,
        width: 110,
        frozen: true,
}

I tried many different Things but nothing would work.

I also tried const myEvent: any = new Event('contextmenu'); as the button Event but it also did nothing. Also nothing showed up in the Console