How to appendChild in all the elements in loop having specific Text in javascript?

I am creating a Dynamic Tree of elements by using an Array Values maintaining the level by attribute value of Parent, which match from the element Button Text
I later on use as accordion.

here the issue coming is, it does not create element for all the elements having that specific Text

I have even used the For Loop and then try to insert with nextElementSibling as you can see in below function

function appendSymptomToForm(symptom, symptomElements, container) {
    if (symptom.parent === 'none') {
        symptomForm.appendChild(container);
    } else {
        
var aTags = document.getElementsByTagName("button");
var searchText = symptom.parent;
var found;

for (var i = 0; i < aTags.length; i++) {    
  if (aTags[i].textContent == searchText) {  

    found = aTags[i].nextElementSibling;    
            found.insertBefore(container, found.nextElementSibling);
  }
}
    }
    console.log(`Symptom ${symptom.text} appended to the form.`);
}

Below I have created the fiddle which give you full understanding, that I have 3 Levels in the array named, window.symptomData.
Every element add according to its Parent Attribute

for example note second Level B:

{ depth: 3, text: ‘Third level B’, parent: ‘second level B’, tooltip:
‘Descrie simptomele după o perioadă de ore’ }

Here you will see that there are multiple buttons of second Level B but it add Third Level only under the Last Element

Please refer the function appendSymptomToForm in the fiddle, where
it does appendChild

Working Fiddle

window.symptomData = [
    { depth: 0, text: 'De aproximativ', parent: 'none', tooltip: 'Perioadă nespecificată de timp, folosită pentru estimări' },
    { depth: 1, text: '1', parent: 'De aproximativ', tooltip: 'Al doilea nivel de timp estimat, similar cu primul' },
    { depth: 1, text: '2', parent: 'De aproximativ', tooltip: 'Al doilea nivel de timp estimat, similar cu primul' },
    { depth: 1, text: '3', parent: 'De aproximativ', tooltip: 'Al doilea nivel de timp estimat, similar cu primul' },
    { depth: 1, text: '4', parent: 'De aproximativ', tooltip: 'Al doilea nivel de timp estimat, similar cu primul' },
    
    { depth: 2, text: 'second level A', parent: '1', tooltip: 'Durată exprimată în ore' },
    { depth: 2, text: 'second level A', parent: '1', tooltip: 'Durată exprimată în zile' },
    { depth: 2, text: 'second level A', parent: '1', tooltip: 'Durată de timp' },    
    { depth: 2, text: 'second level A', parent: '1', tooltip: 'Durată exprimată în luni' },
    { depth: 2, text: 'second level A', parent: '1', tooltip: 'Durată exprimată în ani' },

    { depth: 2, text: 'second level B', parent: '2', tooltip: 'Durată exprimată în ore' },
    { depth: 2, text: 'second level B', parent: '2', tooltip: 'Durată exprimată în zile' },
    { depth: 2, text: 'second level B', parent: '2', tooltip: 'Durată exprimată în săptămâni' },
    { depth: 2, text: 'second level B', parent: '2', tooltip: 'Durată exprimată în luni' },
    { depth: 2, text: 'second level B', parent: '2', tooltip: 'Durată exprimată în ani' },

    { depth: 2, text: 'second level B', parent: '3', tooltip: 'Durată exprimată în ore' },
    { depth: 2, text: 'second level B', parent: '3', tooltip: 'Durată exprimată în zile' },
    { depth: 2, text: 'second level B', parent: '3', tooltip: 'Durată exprimată în săptămâni' },
    { depth: 2, text: 'second level B', parent: '3', tooltip: 'Durată exprimată în luni' },
    { depth: 2, text: 'second level B', parent: '3', tooltip: 'Durată exprimată în ani' },
    
    { depth: 2, text: 'second level B', parent: '4', tooltip: 'Durată exprimată în ore' },
    { depth: 2, text: 'second level B', parent: '4', tooltip: 'Durată exprimată în zile' },
    { depth: 2, text: 'second level B', parent: '4', tooltip: 'Durată exprimată în săptămâni' },
    { depth: 2, text: 'second level B', parent: '4', tooltip: 'Durată exprimată în luni' },
    { depth: 2, text: 'second level B', parent: '4', tooltip: 'Durată exprimată în ani' },
    
    { depth: 3, text: 'Third level A', parent: 'second level A', tooltip: 'Descrie simptomele după o perioadă de ore' },
    { depth: 3, text: 'Third level A', parent: 'second level A', tooltip: 'Descrie simptomele după o perioadă de zile' },
    { depth: 3, text: 'Third level A', parent: 'second level A', tooltip: 'Descrie simptomele după o perioadă de săptămâni' },
    { depth: 3, text: 'Third level A', parent: 'second level A', tooltip: 'Descrie simptomele după o perioadă de luni' },
    { depth: 3, text: 'Third level A', parent: 'second level A', tooltip: 'Descrie simptomele după o perioadă de ani' },
    
    { depth: 3, text: 'Third level B', parent: 'second level B', tooltip: 'Descrie simptomele după o perioadă de ore' },
    { depth: 3, text: 'Third level B', parent: 'second level B', tooltip: 'Descrie simptomele după o perioadă de zile' },
    { depth: 3, text: 'Third level B', parent: 'second level B', tooltip: 'Descrie simptomele după o perioadă de săptămâni' },
    { depth: 3, text: 'Third level B', parent: 'second level B', tooltip: 'Descrie simptomele după o perioadă de luni' },
    { depth: 3, text: 'Third level B', parent: 'second level B', tooltip: 'Descrie simptomele după o perioadă de ani' },
];

// script.js
document.addEventListener('DOMContentLoaded', function() {
    console.log('DOMContentLoaded event triggered, initializing symptoms setup...');
    const symptomData = window.symptomData; // Now pulling from the global variable set by sd.js

    const symptomForm = document.getElementById('symptomForm');
    if (!symptomForm) {
        console.error('Failed to find symptom form on the page.');
        return;
    }

    let symptomElements = {};
    const tooltipDiv = document.createElement('div');
    setupTooltipDiv(tooltipDiv);
    document.body.appendChild(tooltipDiv);

    symptomData.forEach(symptom => {
        const container = document.createElement('div');
        setupSymptomContainer(symptom, container);

        const button = document.createElement('button');
        setupSymptomButton(symptom, button, container);

        const childrenContainer = document.createElement('div');
        setupChildrenContainer(childrenContainer, container);

        symptomElements[symptom.text] = { button, container, childrenContainer };

        setupButtonEvents(button, childrenContainer);
        appendSymptomToForm(symptom, symptomElements, container);
    });

    setupSendButton();
});

function setupTooltipDiv(tooltipDiv) {
    tooltipDiv.id = 'tooltipDiv';
    tooltipDiv.style = "position: fixed; bottom: 20px; left: 20px; padding: 10px; " +
                       "background-color: rgba(0, 0, 0, 0.75); color: white; border-radius: 5px; display: none;";
    console.log('Tooltip div setup completed.');
}

function setupSymptomContainer(symptom, container) {
    container.className = `symptom-instance depth-${symptom.depth}`;
    console.log(`Setup container for symptom: ${symptom.text}`);
}

function setupSymptomButton(symptom, button, container) {
    button.textContent = symptom.text;
    button.className = 'symptom-button';
    button.title = symptom.tooltip;
    container.appendChild(button);
    console.log(`Button created for symptom: ${symptom.text}`);
}

function setupChildrenContainer(childrenContainer, container) {
    childrenContainer.className = 'sub-symptoms hidden';
    container.appendChild(childrenContainer);
    console.log('Children container setup completed.');
}

function setupButtonEvents(button, childrenContainer) {
    button.onclick = () => {
        childrenContainer.classList.toggle('hidden');
        button.classList.toggle('pressed');
        scrollToButton(button);
        console.log(`Visibility toggled for: ${button.textContent}, New state: ${childrenContainer.className}`);
    };
    button.addEventListener('touchstart', () => showTooltip(button, button.title), { passive: true });
    button.addEventListener('touchend', hideTooltip, { passive: true });
}

function appendSymptomToForm(symptom, symptomElements, container) {
       if (symptom.parent === 'none') {
        symptomForm.appendChild(container);
    } else {
        
var aTags = document.getElementsByTagName("button");
var searchText = symptom.parent;
var found;

for (var i = 0; i < aTags.length; i++) {    
  if (aTags[i].textContent == searchText) {  

    found = aTags[i].nextElementSibling;    
            found.insertBefore(container, found.nextElementSibling);
  }
}
    }
    console.log(`Symptom ${symptom.text} appended to the form.`);
}

function scrollToButton(button) {
    const buttonRect = button.getBoundingClientRect();
    const visibleAreaStart = window.innerHeight / 4;
    const scrollYOffset = buttonRect.top - visibleAreaStart + window.scrollY;
    window.scrollTo({
        top: scrollYOffset,
        behavior: 'smooth'
    });
    console.log(`Scrolled to button: ${button.textContent}`);
}

function setupSendButton() {
    const sendButton = document.createElement('button');
    sendButton.textContent = 'Copiaza Selectate';
    sendButton.addEventListener('click', () => {
        const selectedSymptoms = Array.from(document.querySelectorAll('.symptom-button.pressed'))
            .map(btn => btn.textContent.trim())
            .join(', ');
        navigator.clipboard.writeText(selectedSymptoms)
            .then(() => {
                console.log('Selected symptoms copied to clipboard.');
                alert('Selected symptoms copied to clipboard.');
            })
            .catch(err => {
                console.error('Error copying to clipboard:', err);
                alert('Failed to copy symptoms. See console for errors.');
            });
    });
    document.body.appendChild(sendButton);
    console.log('Send button setup completed.');
}

function showTooltip(button, text) {
    const tooltipDiv = document.getElementById('tooltipDiv');
    tooltipDiv.textContent = text;
    tooltipDiv.style.display = 'block';
    console.log(`Tooltip shown for ${button.textContent}: ${text}`);
}

function hideTooltip() {
    const tooltipDiv = document.getElementById('tooltipDiv');
    tooltipDiv.style.display = 'none';
    console.log('Tooltip hidden.');
}
    <div id="symptomForm">
    </div>

Calling Yahoo Finance API in RapidAPI via axios returns error 500

I’m subscribed to the Yahoo Finance API via RapidAPI and I’m calling the API using axios like this:

 const apiKey = ref([MY API KEY])
 const apiURL = ref('https://yahoo-finance127.p.rapidapi.com')

 const headers = ref({
   'x-rapidapi-key': apiKey.value,
   'x-rapidapi-host': apiURL.value
 })

 axios.get(apiURL.value + '/price/NVDA', { headers: headers.value })
   .then(response => console.log(response.data.data))
   .catch(error => console.error(error.message))

Any idea why this is returning an error 500?

ReactJS: Moving to a new page from auto refresh page not working

I auto a refresh a page (say Page1) in every 5 second interval using setTimeout and callback function.

When I move to a new page (say Page2), its going new page and after few seconds again returning to Page1.

I tried window beforeunload event listener, react useBeforeUnload, nothing works. Here is the minimal code to reproduce the issue.

const SampleTestStats = () => {
  let allowStateChange = true;
  let sampleTimeOut = null;
  let SAMPLE_TEST_STATS_REFRESH_TIME = 5000;
  const [result, setResult] = useState([{ tid: 0, tcname: "none"},]);

  const user = JSON.parse(localStorage.getItem('user'));
    
  function refreshPage() {
    sampleTimeOut = setTimeout(refreshPage, SAMPLE_TEST_STATS_REFRESH_TIME);
    window.location.href = "/components/view/sampletest/stats";
  }

  useBeforeUnload(
    React.useCallback(() => {
      console.log('In useBeforeUnload');
      allowStateChange = false;
      clearTimeout(sampleTimeOut);
    })
  );

  const fetchTestStats = async () => {
    try {
      const res = await axios.post('/test/current/active/data', 
      {uid: user.uid, email: user.email} ,
      {headers: {
        'Authorization': user.jwt,
      }}).then( 
      function (res) {
        if(res.status === 200) {
          if(allowStateChange) {
            let newResult = {tid: res.data[0].tid, tcname: res.data[0].testname};
            setResult([newResult]);
          }
        }
      });
    }catch(err) {
    }
  }

  useEffect ( () => {
    fetchTestStats();
  }, []); 
  
  sampleTimeOut = setTimeout(refreshPage, SAMPLE_TEST_STATS_REFRESH_TIME);
  return (`My simple refresh test page, test id = ${result[0].tid} and test name = ${result[0].tcname}.`);
}

Can somebody please help me, when I move to a new page Page2 again automatically it should not come back to old page Page1.

Solid JS Trouble Passing Information from Child to Child

I’ve created a card component which has it’s background image set from it’s parent component. These cards act as buttons to open modals with more details and share an image. I don’t want the modal constrained to the size of the card. I want it to appear over the rows of cards.

I tried to:

  1. I set a signal in my parent component.
  2. Then passed the setter for the signal into my card component.
  3. In my card component I use the setter to set the new signal value upon opening the modal.
  4. Then passed the new signal value into the modal as a prop.
  5. I use the prop value for the modal image.

PARENT COMPONENT

export const ClassSelect = () => {
    const [classes] = createResource(getClasses);
    const [modalToggle, setModalToggle] = createSignal(false);

//--- 1.SET THE SIGNAL ---
    const [modalBackground, setModalBackground] = createSignal('');

    return (
        <div className="classSelectLayoutStyles">
            <h1 className="pl-4 text-white font-extrabold text-shadow-main text-header-md shadow-fuchsia-900">
                Select a Class!
            </h1>
            <div className="classSelectGridStyles">
                <For each={classes()}>{(_class) =>
                    <SelectionCard
                    name={_class.class_name}
                    backgrounds={classBackgrounds}
                    cardShrink={"classBackgroundShrink"}
                    imgShrink={"classImageShrink"}
                    modalToggle={modalToggle}
                    setModalToggle={setModalToggle}
//--- 2.PASS THE SETTER ---
                    modalBackground={modalBackground}
                    **setModalBackground={setModalBackground}**
                />
                }</For>
            </div>
            <Show when={modalToggle()}>
//--- 3.PASS THE NEW SIGNAL VALUE ---
                <ClassDescriptionModal setModalToggle={setModalToggle} modalBackground={modalBackground}/>
            </Show>
        </div>
    );
};

CARD COMPONENT

export const SelectionCard = (props) => {
  const selectionName = props.name;
  const selectionBackgrounds = props.backgrounds;
  const cardShrink = props.cardShrink;
  const imgShrink = props.imgShrink;
  const setModalToggle = props.setModalToggle;
  const modalBackground = props.modalBackground;
  const setModalBackground = props.setModalBackground;

  const selectionBackground = selectionBackgrounds[selectionName.toLowerCase().replace(/s/g, "")];

  const createSelectionClass = (selectionBackgrounds) => {
    if (selectionBackgrounds) {
      return (
        imgShrink +
        " " +
        selectionBackground +
        " saturate-30 hover:saturate-100 flex justify-center w-full h-full"
      );
    } else {
      return "selectionBackground bg-skulls bg-gray-900 saturate-30 hover:saturate-100 flex justify-center w-full h-full";
    }
  };
//--- 4.SET THE NEW SIGNAL VALUE UPON OPENING THE MODAL ---
  const openModal = () => {
    setModalBackground(selectionBackground);
    setModalToggle(true);
  };

  return (
    <div
      onClick={openModal}
      className={
        cardShrink
          ? "baseLayoutStyles " + cardShrink
          : "baseLayoutStyles cardShrink"
      }
    >
      <div className={createSelectionClass(selectionBackgrounds)}>
        <div className="text-center text-white text-xl font-extrabold pt-4 bg-colorHeader rounded-t w-full h-1/6 text-shadow shadow-gray-800">
          {selectionName}
        </div>
      </div>
    </div>
  );
};

MODAL COMPONENT

export const ClassDescriptionModal = (props) => {
  const setModalToggle = props.setModalToggle;
  const modalBackground = props.modalBackground;

  const getModalClass = (modalBackground) => {
    return "w-1/2 h-full "+ modalBackground +" rounded-l-lg";
  }

  const closeModal = () => setModalToggle(false);

  return (
    <div onClick={closeModal} className="fixed top-0 left-0 right-0 bottom-0 bg-black bg-opacity-10 z-50 flex items-center justify-center">
      <div className={"baseLayoutStyles flex flex-row absolute top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2 w-5/6 h-5/6 bg-skulls bg-gray-900 rounded-lg"}>

//--- 5.MODAL IMAGE ---
        <div className={getModalClass(modalBackground)}></div>

        <div className="w-1/2 h-full p-4">
          <button
              onClick={closeModal}
              className="px-4 py-2 bg-fuchsia-900 text-white rounded"
          >
            Close
          </button>
         </div>
      </div>
    </div>
  );
};

Disabling inputs in Bootstrap based on toggle switch selection?

Reading through the documentation, I’m trying to find a way in bootstrap 5 to disable other input fields if a toggle switch input is set to ‘off’?

Parent element is #member_form, switch is ‘toggleswitch’.

The following will disable the field when anything is the form is clicked. But I’d like to switch from enabled to disabled and back based on the the toggleswitch element?

    $(document).on('click', '#member_form', function(){
        $("#input_name").attr('disabled','disabled');
    });

Any suggestions?

Get updated data after updating it and filtering React JS

I have an existing code here that update employee and show the updated employee. I also have a filter dropdowns that filters the data but after filtering the data and updating it. It only shows the updated filtered data due to the setEmployees(newEmployees).

function updateEmployee(index, employee) {
        const newEmployees = employees.slice();
        newEmployees[index] = employee;
        setEmployees(newEmployees);
        //setEmployees(updatedEmployee and all the data)
 }

What I want to happen is after updating it. The previous data is still there as well as the updated one

Understanding the use of bitwise-operator in ternary-operator

I’m just learning how to code and while studying sorting algorithms I was asked to “see if you can combine the two lines in your if and else blocks into one line to make your code look a little cleaner.”

const merge = (leftArray, rightArray) => {
  const sortedArray = [];
  while (leftArray.length > 0 && rightArray.length > 0) {
    if (leftArray[0] < rightArray[0]) {
      sortedArray.push(leftArray[0]);
      leftArray.shift();
    } else {
      sortedArray.push(rightArray[0]);
      rightArray.shift();
    }
  }

The first thing that came to my mind was to use ternary operator, and then, for some reason (never saw that being done before), I thought that I could just take the two actions for every condition, join them with ‘&&’ and that would work.

But I had a typo!! I wrote ‘&’ instead of ‘&&’, and for my surprise, even with that it worked as intended. But I have no idea why, can’t find anything that explains how this actually works and why. (Below is all the code and the part I re-wrote).

const mergeSort = (startArray) => {
  const length = startArray.length;
  if (length === 1) {
    return startArray;
  }
  
  const mid = Math.floor(length / 2);
  const leftArray = startArray.slice(0, mid);
  const rightArray = startArray.slice(mid, length);

  return merge(mergeSort(leftArray), mergeSort(rightArray))
}

const merge = (leftArray, rightArray) => {
  const sortedArray = [];
  while (leftArray.length > 0 & rightArray.length > 0) {
    leftArray[0] < rightArray[0] ? 
    sortedArray.push(leftArray[0]) & leftArray.shift() : 
    sortedArray.push(rightArray[0]) & rightArray.shift()
  }
  
  return sortedArray.concat(leftArray).concat(rightArray);
}

I would appreciate if some could shed some light to this.

Background video loop seamless in Firefox but not Chrome?

I have a website that I have been working on that has a responsive background video. How it is supposed to work:

  1. Detect the screen size of the device (mobile screen vs. larger “desktop” screen)
  2. Preload the correct video on the blank htmtl background video element
  3. Quickly & gently fade the video element in once it loads
  4. Loop the video indefinitely, with no further fading or sharp transitions
  5. Monitor for screen size changes and reload a different video if necessary
document.addEventListener('DOMContentLoaded', function() {
    var vid = document.querySelector('video'); // Adjust this selector to target your video element specifically.
    var mobileReload = 0;
    var initialLoad = 0;

    function updateVideoSource() {
        if (window.innerWidth <= 768 && mobileReload == 0) {
            vid.classList.remove('is-loaded'); // Remove class before setting the correct source
            mobileReload = 1;
            vid.src = '/wp-content/uploads/2024/04/Homepage-Video-Mobile.webm'; // Add your mobile video path here.
            vid.removeAttribute("loop");
            vid.classList.add('is-loaded'); // Add class after setting the correct source
            if (initialLoad == 0) {
                initialLoad = 1;
                vid.style.opacity = 0;
                vid.oncanplaythrough = function() {
                    setTimeout(function() {
                        fade(vid);
                    }, 0.01);
                };
            }
        }
        if (window.innerWidth > 768) {
            vid.classList.remove('is-loaded'); // Remove class before setting the correct source
            mobileReload = 0;
            vid.src = '/wp-content/uploads/2024/04/Homepage-Video-Desktop.webm'; // Add your default video path here.
            vid.removeAttribute("loop");
            vid.classList.add('is-loaded'); // Add class after setting the correct source
            if (initialLoad == 0) {
                initialLoad = 1;
                vid.style.opacity = 0;
                vid.oncanplaythrough = function() {
                    setTimeout(function() {
                        fade(vid);
                    }, 0.01);
                };
            }
        } else {
            // No update to avoid reloading when scrolling on mobile due to address bar resizing
        }
    }

    function fade(element) {
        var op = 0;
        var timer = setInterval(function() {
            if (op >= 1) clearInterval(timer);
            element.style.opacity = op;
            element.style.filter = 'alpha(opacity=' + op * 100 + ")";
            op += op * 0.1 || 0.1;
        }, 10);
    }
    // Add event listener for the 'ended' event to loop the video
    vid.addEventListener('ended', function() {
        vid.currentTime = 0; // Reset video to beginning
              vid.play();
    });

    window.addEventListener('resize', updateVideoSource);
    updateVideoSource(); // Call it on initial load as well.
});

The problem I am having is with Step 4. The loop works perfectly on the browser I use, develop, and test on (Firefox, and iOS Safari). But most consumers afaik use Chrome and there is an ugly gray fade stutter for about half a second on Chrome when the video loops. Both videos are .WEBM and < 3mb. Any ideas?

As you can see I’m trying a couple of different things to fix it but neither are working.

I read that some browsers handle loops differently so I started by removing the loop attribute from the video. I know it would be easier to do that in the actual HTML but I’m kind of cheating and using a WP Cover Block as the video bg element and manipulating it with JS so this is just easier. Than I add an event listener to manually detect when the video ends and restart it. This doesn’t work- as in, it works exactly the same as having the loop attribute in the html and doesn’t fix the issue.

I also added the variable initialLoad to prevent the fade event from running more than once, that isn’t working either, so I’m assuming it’s something to do with Chrome here.

How can I terminate a function process when an event occurred

everyone, there is a task, where I need to terminate a function process when an event occurs.

Normally, when throwing an error in anything inside the process, for example, throwing an error from the delay in any step of the next steps, the process will terminate without any problems.

But if I created an event listener, that’s responsible for terminating the process. If I make throw error inside it the process will keep running without any problems in the process. and it will be considered as an Unhandled Rejection or DOMException.

  • Because the error thrown was made inside the listener context and not the main function context everything will keep going without any problems so to make it clear I will explain what I mean with some examples

  • In The first example I’m tring to terminate the process within the event listener, but the process will continue normally without problems and it is treated as an Unhandled Rejection or DOMException

(() => {
  class EventEmitter {
    constructor() {
      this.events = {};
    }

    on(event, listener) {
      if (!this.events[event]) {
        this.events[event] = [];
      }

      this.events[event].push(listener);

      // Return a function that can be used to remove this specific listener
      return () => {
        this.off(event, listener);
      };
    }

    emit(event, ...args) {
      if (this.events[event]) {
        this.events[event].forEach(listener => listener(...args));
      }
    }

    removeListeners(event) {
      delete this.events[event];
    }

    off(event, listener) {
      if (this.events[event]) {
        this.events[event] = this.events[event].filter(existingListener => existingListener !== listener);
      }
    }
  }

  const delay = (ms) => new Promise(resolve => setTimeout(resolve, ms));
  const processEmitter = new EventEmitter();

  async function startProcess(params) {
    try {
      const removeListener = processEmitter.on('stop', async () => {
        removeListener(); 
        throw new Error(`Process stopped due to external event`); 
      });

      await delay(2000);
      console.log('finished step 1');
      await delay(2000);
      console.log('finished step 2');
      await delay(2000);
      console.log('finished step 3');
      await delay(2000);
      console.log('finished step 4');
      await delay(2000);
      console.log('finished step 5');
      await delay(2000);
      console.log('finished step 6');
    } catch (err) {
      console.log('Process terminated:', err.message);
    } finally {
      console.log('done !!')
    }
  }

  startProcess();

  setTimeout(() => {
    processEmitter.emit('stop');
  }, 5000);
})();

  • In The following example I’m trying to throw the error with proxy when the value changed
(() =>  {
  const processEmitter = new EventEmitter();
  async function startProcess(params) {
    try {
      // use proxy as a middleware when the value change
      const abortHandler = new Proxy({ msg: '' }, {
        set(target, key, value) {
          target[key] = value;
          throw new Error(value);
        }
      });

      const removeListener = processEmitter.on('stop', async () => {
        removeListener(); 
        abortHandler.msg = `Process stopped due to external event`; 
      });

      await delay(2000);
      console.log('finished step 1');
      await delay(2000);
      console.log('finished step 2');
      await delay(2000);
      console.log('finished step 3');
      await delay(2000);
      console.log('finished step 4');
      await delay(2000);
      console.log('finished step 5');
      await delay(2000);
      console.log('finished step 6');
    } catch (err) {
      console.log('Process terminated:', err.message);
    } finally {
      console.log('done !!')
    }
  }

  startProcess();

  setTimeout(() => {
    processEmitter.emit('stop');
  }, 5000);
})();
  • In the following example I’m trying to terminate the process with function that will throw the error when called but also it doesn’t work because it’s running inside the listener context not at the main function context
(() =>  {
  const processEmitter = new EventEmitter();
  async function startProcess(params) {
    try {
      // use proxy as a middleware when the value change
      const reject = msg => {
        throw new Error(msg);
      } 

      const removeListener = processEmitter.on('stop', async () => {
        removeListener(); 
        reject(`Process stopped due to external event`); 
      });

      await delay(2000);
      console.log('finished step 1');
      await delay(2000);
      console.log('finished step 2');
      await delay(2000);
      console.log('finished step 3');
      await delay(2000);
      console.log('finished step 4');
      await delay(2000);
      console.log('finished step 5');
      await delay(2000);
      console.log('finished step 6');
    } catch (err) {
      console.log('Process terminated:', err.message);
    } finally {
      console.log('done !!')
    }
  }

  startProcess();

  setTimeout(() => {
    processEmitter.emit('stop');
  }, 5000);
})();
  • I also tried to wrap the whole function in a Promise and use reject with hope to stop the process when call reject but because of the Promise sync nature the process will keep going after calling reject without any problems
(() => {
  const processEmitter = new EventEmitter();
  async function startProcess(params) {
    return new Promise(async (_resolve, reject) => {
      try {
        const removeListener = processEmitter.on('stop', async () => {
          removeListener();
          reject(`Process stopped due to external event`);
        });

        await delay(2000);
        console.log('finished step 1');
        await delay(2000);
        console.log('finished step 2');
        await delay(2000);
        console.log('finished step 3');
        await delay(2000);
        console.log('finished step 4');
        await delay(2000);
        console.log('finished step 5');
        await delay(2000);
        console.log('finished step 6');
      } catch (err) {
        console.log('Process terminated:', err.message);
      } finally {
        console.log('done !!')
      }
    })
  }

  startProcess();

  setTimeout(() => {
    processEmitter.emit('stop');
  }, 5000);
})();

  • I also tried intervals but the result is still the same because throwing the error will be within the callback context not at the main function context

  • The solution I found is a repetitive solution, which is the normal case, but I need to reduce repeating the same steps / code more than once, which is to use a variable or AbortController. However, these solutions will also be invalid, and it is not the best solution, because when I make a call to controller.abort() Also, step 3 is executed even though it is not supposed to be executed, because the stop event is called in the first second of the function call, meaning that step 3 is executed while it’s suppose to not, and after that it goes to step 4 and wow it findout that signal is aborted, so it throws an error + that this step has a lot of repeativitive code, meaning that If each delay represents a function alone with different code, then repeat the same step for all of them

(() =>  {
  const delay = (ms, signal) => {
    if (signal?.aborted) {
      throw new Error(signal?.reason || 'Aborted')
    }

    return new Promise(resolve => setTimeout(resolve, ms))
  };

  const processEmitter = new EventEmitter();
  const controller = new AbortController();
  const { signal } = controller;

  async function startProcess(params) {
    try {
      const removeListener = processEmitter.on('stop', async () => {
        removeListener();
        controller.abort('Process stopped due to external event');
      });

      await delay(2000, signal);
      console.log('finished step 1');
      await delay(2000, signal);
      console.log('finished step 2');
      await delay(2000, signal);
      console.log('finished step 3');
      await delay(2000, signal);
      console.log('finished step 4');
      await delay(2000, signal);
      console.log('finished step 5');
      await delay(2000, signal);
      console.log('finished step 6');
    } catch (err) {
      console.log('Process terminated:', err.message);
    } finally {
      console.log('done !!')
    }
  }

  startProcess();

  setTimeout(() => {
    processEmitter.emit('stop');
  }, 5000);
})();
  • The last solution is variable, which is also the same idea as aborted signals, but the difference is that abort controller signals make the application logic better and more elegant, and you can use it in differnt ways.
(() =>  {
  const delay = (ms, err) => {
    if (err) {
      throw err;
    }

    return new Promise(resolve => setTimeout(resolve, ms))
  };

  const processEmitter = new EventEmitter();

  async function startProcess(params) {
    try {
      let err = null;
      const removeListener = processEmitter.on('stop', async () => {
        removeListener();
        err = new Error('Process stopped due to external event');
      });

      await delay(2000, err);
      console.log('finished step 1');
      await delay(2000, err);
      console.log('finished step 2');
      await delay(2000, err);
      console.log('finished step 3');
      await delay(2000, err);
      console.log('finished step 4');
      await delay(2000, err);
      console.log('finished step 5');
      await delay(2000, err);
      console.log('finished step 6');
    } catch (err) {
      console.log('Process terminated:', err.message);
    } finally {
      console.log('done !!')
    }
  }

  startProcess();

  setTimeout(() => {
    processEmitter.emit('stop');
  }, 5000);
})();

How to have an app window not take away focus like in Windows 11 emoji app

Let’s say you have Notepad open in Windows 11. You hit Win+. shortcut to open the emoji app. Click on an item and it pastes it in wherever your | symbol is in your Notepad.

Essentially we have a window we can control like any normal piece of UI, without removing focus from the previously focused window (Notepad in our case).

How would I be able to replicate this behavior in Electron?

enter image description here

Extra note:
This is something that could potentially be achieved by making the app window unfocusable and adding in a bunch of code that would process global inputs, but that would result in quite an overengineered solution. I’m curious if there’s anything out of the box that would support this behavior.

Any help much appreciated!

Hide the button if value of variable is missing

I am a novice in Javascript and I have a variable inside a for loop like below:

myTBody2 += `
<tr><td><button class="btn btn-sm btn-primary" onclick="myFetcher('${data._links.first.href}')">First</button></td>
  <td><button class="btn btn-sm btn-primary" onclick="myFetcher('${data._links.self.href}')">Current</button></td>
  <td><button class="btn btn-sm btn-primary" onclick="myFetcher('${data._links.next.href}')">Next</button> </td>
  <td><button class="btn btn-sm btn-primary" onclick="myFetcher('${data._links.last.href}')">Last </button></td>
</tr>`;

Now, say, if the value of ${data._links.next.href} is undefined or missing, how do I hide it?
Is there a javascript (vanilla JS) inline (one liner) function available to do that?
Any pseudo/pointers/solution will be greatly appreciated.

v-data-table showing up as empty despite items being in the backing array

I have this function fetchItemData that fetches data from the DB and assigns it to itemsList, which is bound to items of v-data-table. The v-data-table shows the new data perfectly fine on other events, like a change in sort parameters or items-per-page. But when searchText changes (@input event on v-text-field), despite the data being returned perfectly fine and assigned to itemsList with no problems, it does not show up in the table at all and I get the no-data screen.

Even when I added a debug function printItems that simply ran console.log(this.itemsList) upon the table being clicked, it shows that the itemsList = a list of items matching the search query. They’re just not being shown.

I have a reduced version of my code here with extraneous things removed:

Template:

<template>
  <v-card class="pa-4">
    <v-data-table
      v-model="selected"
      v-model:sort-by="sortBy"
      v-model:items-per-page="itemsPerPage"
      v-model:page="pageNum"
      :search="searchText"
      :headers="headers"
      :items="itemsList"
      :loading="itemsLoading"
      :pageText="allFetched ? '{0}-{1} of {2}' : '{0}-{1} of more'"
      :last-icon="allFetched ? '$last' : ''"
      @update:itemsPerPage="fetchItemData"
      @update:sortBy="fetchItemData"
      @update:page="fetchNextPage"
      item-key="id">
      <template v-slot:top>
        <v-container fluid>
          <v-row class="pb-2">
            <v-text-field
              v-model="searchText"
              label="Search"
              prepend-inner-icon="mdi-magnify"
              bg-color="transparent"
              variant="underlined"
              density="compact"
              single-line
              hide-details
              clearable
              @input="fetchItemData" />
            <v-btn
              v-if="selected && selected.length"
              @click="() => {}"
              color="error"
              size="small"
              class="ml-4 float-right align-self-end">
              Delete Selected
            </v-btn>
          </v-row>
        </v-container>
      </template>

      <template v-slot:loading>
        <v-skeleton-loader type="table-tbody" />
      </template>

      <template v-slot:no-data>
        <div class="mt-6 text-subtitle-1">No data found</div>
        <v-btn
          class="mt-2 mb-6"
          color="primary"
          size="small"
          variant="flat"
          prepend-icon="mdi-refresh"
          @click="fetchItemData"> // <- the problem
          Retry
        </v-btn>
      </template>

      templates for item rows etc...
    </v-data-table>
  </v-card>
</template>

Script:

import { items } from '@/services/items.js'

export default {
  data: () => ({
    itemsList: [],
    itemsLoading: false,
    itemsPerPage: 10,
    pageNum: 1,
    searchText: '',
    selected: [],
    sortBy: [{ key: 'updatedAt', order: 'desc' }],
    allFetched: false
  }),
  methods: {
    fetchItemData() {
      var numToFetch = this.itemsPerPage + 1 //get one extra so next button remains active if there exist items past curr page

      var searchParams = {
        query: this.searchText
      }

      var pageParams = {
        size: numToFetch,
        sort: this.sortBy[0]
      }

      this.itemsLoading = true
      items
        .list(searchParams, pageParams)
        .then((response) => {
          if (response.data) {
            this.itemsList = response.data?.items || []
          } else {
            this.itemsList = []
          }

          //if items received is less than num to fetch, we've reached end
          if (response.data?.items && response.data.items.length < numToFetch)
            this.allFetched = true

          this.itemsLoading = false
          return this.itemsList
        })
        .catch(() => {
          this.itemsList = []
          this.itemsLoading = false
        })
    },
    fetchNextPage() {...}
  computed: {
    headers() {
      return [
        row headers...
      ]
    }
  },
  created() {
    this.fetchItemData()
  },
  name: 'ItemsTable'
}

Any help would be much appreciated: thank you!

Angular material table multiple header rows mapping

There is a sample of data table which consists of multiple header rows

|          |  Song 1        | Song 2         | Song 3         |
|---------------------------|----------------|----------------|
|  Artist  | Perc % | Val $ | Perc % | Val $ | Perc % | Val $ |
|-------------------------------------------------------------|
| Q1       | 10%    | 200$  | 15%    | 250$  |        |       |  
| BW       | 10%    | 200$  | 10%    | 200$  |        |       |
| POD      |  5%    | 150$  | 10$    | 200$  |        |       |
|          |        |       |        |       |        |       |
| SUM      | 25%    | 550$  | 25%    | 650$  |        |       |


with provided dataset

{
    "data": [{
            "artistId": "A001",
            "artistName": "Q1",
            "songs": [{
                    "songId": "S001",
                    "songName": "Song 1",
                    "percentage": "10%",
                    "value": "$200"
                },
                {
                    "songId": "S002",
                    "songName": "Song 2",
                    "percentage": "15%",
                    "value": "$250"
                }
            ]
        },
        {
            "artistId": "A002",
            "artistName": "BW",
            "songs": [{
                    "songId": "S001",
                    "songName": "Song 1",
                    "percentage": "10%",
                    "value": "$200"
                },
                {
                    "songId": "S002",
                    "songName": "Song 2",
                    "percentage": "10%",
                    "value": "$200"
                }
            ]
        },
        {
            "artistId": "A003",
            "artistName": "POD",
            "songs": [{
                    "songId": "S001",
                    "songName": "Song 1",
                    "percentage": "5%",
                    "value": "$150"
                },
                {
                    "songId": "S002",
                    "songName": "Song 2",
                    "percentage": "10%",
                    "value": "$200"
                }
            ]
        }
    ],
    "summary": [{
            "songName": "Song 1",
            "totalPercentage": "25%",
            "totalValue": "$550"
        },
        {
            "songName": "Song 2",
            "totalPercentage": "25%",
            "totalValue": "$650"
        }
    ]
}

I’m wondering could provided dataset be mapped in order to get something like on the sample table using angular material table? There is no need for pagination.