vuejs3 array length does not update appropriately

i am trying to build a shop and i try to add items to the cart but for some reason the cart length is wrong as i am trying to be able to add items to the cart without having duplicates in terms of items names. so when i add the same item twice it basically shows only 1 item in the cart if though i have 2.

i tried changing the the addtocart function to recognize items by there id as my items have these properties: id, name, price and quantity. i am trying to count the number of items in the cart by using foreach loop. because when i tries using the javascript array.length function it did not worked. so i am trying now with foreach loop. my code is like this:

shop.vue:

<template>
  <div>
    <h1>Shop</h1>
    Items in cart: {{ cartnum }}
    <div v-for="item in Products" :key="item.id">
      {{ item.name }} {{ item.price }}$
      <button @click="storeCounter.addToCart(item)">Add to Cart</button>
    </div>
  </div>
</template>
<script setup>
import { useCounterStore } from "../stores/counter";
import { computed } from "vue";
import Products from "../db.json";

const storeCounter = useCounterStore();

const cartnum = computed(() => storeCounter.cartnum);
</script>

cart.vue:

<template>
  <div class="cart">
    <h1>Cart</h1>
    <div class="cartitems" v-for="item in storeCounter.cart" :key="item.id">
      {{ item.name }} {{ item.price }}$ {{ item.quantity }}
      <button @click="storeCounter.removeFromCart(item)">X</button>
    </div>
    <h4>total items: {{ cartnum }}</h4>
    <h4>Total Price: {{ cartSum }}</h4>
  </div>
</template>

<script setup>
import { useCounterStore } from "../stores/counter";
import { computed } from "vue";

const storeCounter = useCounterStore();

const cartSum = computed(() => {
  let total = 0;
  storeCounter.cart.forEach((item) => {
    total += item.price * item.quantity;
  });
  return total;
});
const cartnum = computed(() => storeCounter.cartnum);
</script>

pinia file:

import { defineStore } from "pinia";

export const useCounterStore = defineStore("counter", {
  state: () => ({
    cart: [],
  }),
  actions: {
    addToCart(item) {
      let cartItemIndex = this.cart.findIndex((x) => x.id === item.id);

      if (cartItemIndex >= 0) {
        item.quantity++;
      } else {
        this.cart.push(item);
      }
    },
    removeFromCart(id) {
      let cartItemIndex = this.cart.findIndex((x) => x === id);

      if (cartItemIndex >= 0) {
        this.cart.splice(cartItemIndex, 1);
        console.log("Removed from cart");
      }
    },
  },
  getters: {
    cartnum() {
      let total = 0;
      this.cart.forEach((item) => {
        total += item.quantity;
      });
      return total;
    },
  },
});

how do i fix that? thanks in advance!

Is it dangerous to publically show my chrome extension ID?

So i have built an chrome extension with manifest v3. Since it adds some images into the DOM, i wrote a line similar to this:

let imageSource = "chrome-extension://{id}/ExtensionImages/image.png";

I’m thinking about posting the extension on GitHub, but i don’t know if showing the extension id publically on the source code could generate security issues for my extension or even for my computer.

I understand that there are probably better ways to retrieve an image with manifest without using the id, but even if i were to repace this line, anyone could still see my commit history and get the extension id.

Can’t figure out why ‘let’ works but not ‘var’ [duplicate]

I would like to do a simple loop using document querySelectorALL and loop through the list that is returned. Within the loop, I checked other references which declare the loop using (var i = 0; i < btns.length; i++). This gave me the following error:

Uncaught TypeError: Cannot read properties of undefined (reading ‘style’) at HTMLButtonElement.onClick

But when I changed var to let, it works.

Can’t figure out why.

<script>
    // TODO: Add code to check answers to
    const btns = document.querySelectorAll('button');

    for (let i = 0; i < btns.length; i++) {
        btns[i].addEventListener('click', function onClick() {
            btns[i].style.backgroundColor = 'red';
        });
    }
</script>

AppScript Convert URI-encoded string to Array

I am trying to convert a URI-encoded string into an array.

function handleURI(url) {
  var rowData = [];
  var params = url.split('&');
  
  for (var i = 0; i < params.length; i++) {
    var pair = params[i].split('=');
    Logger.log(pair);
    var key = decodeURI(pair[0]);
    var value = decodeURI(pair[1] || '');
    Logger.log(key&"="&value)
    rowData[key] = value;
  }
  return(rowData);
}

function test(){
var dataArray = handleURI('type=deal_update&date_time=2023-12-16T11%3A05%3A11-06%3A00&initiated_from=admin&initiated_by=admin&list=0&contact%5Bid%5D=2');

Logger.log(JSON.stringify(dataArray));
}

In the logger, I can see the values in the “pair” variable, but the values are able to be referenced using pair[0] and pair1.

Here are the logs I getting back

enter image description here

add sound on GSAP scroll

i am using GSAP scroll trigger for animating my message containers like a chatbox i want to play sound with every message appear with that also how i can do that ?
below is the code where i want to implement audio

 <script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.5.0/gsap.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.3.4/ScrollTrigger.min.js"></script>
<script>
  // Ensure GSAP and ScrollTrigger are loaded
  gsap.registerPlugin(ScrollTrigger);

  // Your GSAP animation
  gsap.from(".imageTransition", {
    y: "100%", 
    ease: "easeInOut",
    duration: 2,
    scrollTrigger: {
      trigger: ".triggerPoint",
      scroller: "body",
      start: "top top",
      end: "bottom top",
      pin :true,
    }
  });

  // Create a GSAP timeline
  var timeline = gsap.timeline();

  // Animation 1
  timeline.from(".message1", {
    x: "200px",
    opacity: 0,
    ease: "easeOut",
  });

  // Animation 2 with a stagger of 2s
  timeline.from(".message2", {
    x: "-200px",
    opacity: 0,
    ease: "easeInOut",
  }, "+=3");

  // Animation 3 with a stagger of 2s
  timeline.from(".message3", {
    x: "200px",
    opacity: 0,
    ease: "easeInOut",
  }, "+=3");

  // Animation 4 with a stagger of 2s
  timeline.from(".message4", {
    x: "-200px",
    opacity: 0,
    ease: "easeInOut",
  }, "+=3");
  
   timeline.from(".message5", {
    x: "200px",
    opacity: 0,
    ease: "easeInOut",
  }, "+=3");
  
   timeline.from(".message6", {
    x: "200px",
    opacity: 0,
    ease: "easeInOut",
  }, "+=3");


  // Use the timeline in the ScrollTrigger
  ScrollTrigger.create({
    trigger: ".triggerPoint2",
    start: "200px top",
    end: "bottom top",
    animation: timeline,
    scrub: true,
    pin: true,
    markers : true,
    onComplete: function () {
      gsap.set(".triggerPoint2", { pin: true });
    },
  });
</script>

i have tried using audio sound and also web api of sound on complete of every animation but it didn’t work. i want to play sound everytime the message appear on scrol

How do I extract only the KEYS from an array of objects?

How do I extract only the time range KEYS within an array of objects?

The following is my code:

console.log(tempRemovedTimeRanges);

const removedKeys = tempRemovedTimeRanges
  .flatMap(obj => Object.values(obj))
  .filter(dayArray => dayArray.some(dayObj => Object.keys(dayObj)[0] === elementToInsert))
  .flatMap(dayArray =>
    dayArray
      .filter(dayObj => Object.keys(dayObj)[0] === elementToInsert)
      .flatMap(dayObj => dayObj[elementToInsert].flatMap(range => Object.keys(range)))
  );

console.log(removedKeys);

The code above logs out:

(8) [{…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}]
[]

As you can see the console.log(removedKeys); code logs out an empty array.

Allow me to expand tempRemovedTimeRanges array for you to get an idea of the tempRemovedTimeRanges structure:

(8) [{…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}]
   0: {Wednesdays: Array(2)}
   1: {Fridays: Array(2)}
   2: {Wednesdays: Array(2)}
   3: {Fridays: Array(2)}
   4: {Wednesdays: Array(2)}
   5: {Fridays: Array(2)}
   6: {Wednesdays: Array(2)}
   7: {Fridays: Array(2)}

[]

As you can see, the tempRemovedTimeRanges reveals an array of objects representing days.

Allow me to expand the first object 0: {Wednesdays: Array(2)} further.
Kindly note that all objects share a similar structure as the first object 0: {Wednesdays: Array(2)}

(8) [{…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}]
  0: 
   Wednesdays: Array(2)
      0: {T_04:00_Start: 1703034000, T_08:00_End: 1703048400}
      1: {T_04:00_Start: 1703638800, T_08:00_End: 1703653200}
  1: {Fridays: Array(2)}
  2: {Wednesdays: Array(2)}
  3: {Fridays: Array(2)}
  4: {Wednesdays: Array(2)}
  5: {Fridays: Array(2)}
  6: {Wednesdays: Array(2)}
  7: {Fridays: Array(2)}

The following section represents two time ranges for the Wednesdays object.
The first one is T_04:00_Start to T_08:00_End and T_04:00_Start to T_08:00_End

{T_04:00_Start: 1703034000, T_08:00_End: 1703048400}
{T_04:00_Start: 1703638800, T_08:00_End: 1703653200}

The keys T_04:00_Start, T_08:00_End, T_04:00_Start and T_08:00_End and NOT the values (timestamps) are what I am interesting in capturing for each day in the tempRemovedTimeRanges array

Now going back to my code:

console.log(tempRemovedTimeRanges);

const removedKeys = tempRemovedTimeRanges
  .flatMap(obj => Object.values(obj))
  .filter(dayArray => dayArray.some(dayObj => Object.keys(dayObj)[0] === elementToInsert))
  .flatMap(dayArray =>
    dayArray
      .filter(dayObj => Object.keys(dayObj)[0] === elementToInsert)
      .flatMap(dayObj => dayObj[elementToInsert].flatMap(range => Object.keys(range)))
  );

console.log(removedKeys);

The desired output of console.log(removedKeys); are all the time ranges keys for each day found in the tempRemovedTimeRanges. Those would therefore look like this:

[T_04:00_Start, 
T_08:00_End, 
T_04:00_Start, 
T_08:00_End, 
T_12:00_Start, 
T_16:00_End, 
T_12:00_Start, 
T_16:00_End, 
T_20:00_Start, 
T_00:00_End, 
T_20:00_Start, 
T_00:00_End, 
T_16:00_Start, 
T_20:00_End, 
T_16:00_Start, 
T_20:00_End]

How do I modify my code to do this?

How to call Fragment from my Main Activity?

My fragment details can’t be opened. when I type a word in search and I want to see the details of the word it won’t open. I don’t know where the error lies. Please help me. I’m still new to Android Studio

detail fragment



import android.content.Context;
import android.os.Bundle;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;

import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.webkit.WebView;
import android.widget.ImageButton;
import android.widget.TextView;


public class DetailFragment extends Fragment {

    private String value = "";
    private TextView tvWord;
    private ImageButton btnBookmark;
    private WebView tvWordTranslate;
    private DBHelper mDBHelper;
    private int mDicType;
   

    public DetailFragment() {
        // Required empty public constructor
    }

    public static DetailFragment getNewInstance(String value, DBHelper dbHelper, int dicType){
        DetailFragment fragment = new DetailFragment();
        fragment.value = value;
        fragment.mDBHelper = dbHelper;
        fragment.mDicType = dicType;
        return fragment;
    }


    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        // Inflate the layout for this fragment
        return inflater.inflate(R.layout.fragment_detia, container, false);
    }

    @Override
    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        tvWord = (TextView) view.findViewById(R.id.tvWord);
        tvWordTranslate = (WebView) view.findViewById(R.id.tvWordTranslate);
        btnBookmark = (ImageButton) view.findViewById(R.id.btnBookmark);

        final Word word = mDBHelper.getWord(value, mDicType);
        tvWord.setText(word.key);
        tvWordTranslate.loadDataWithBaseURL(null, word.value,"text/html", "utf-8",null);

        Word bookmarkWord = mDBHelper.getWordFromBookmark(value);
        int isMark = bookmarkWord == null ? 0 : 1;
        btnBookmark.setTag(isMark);

        int icon = bookmarkWord == null ? R.drawable.ic_bookmark_border : R.drawable.ic_bookmark_fill;
        btnBookmark.setImageResource(icon);

        btnBookmark.setOnClickListener(new View.OnClickListener() {
            @Override
             public void onClick(View view) {
                int i = (int) btnBookmark.getTag();
                if (i == 0) {
                    btnBookmark.setImageResource(R.drawable.ic_bookmark_fill);
                    btnBookmark.setTag(1);
                    mDBHelper.addBookmark(word);
                } else if (i == 1) {
                    btnBookmark.setImageResource(R.drawable.ic_bookmark_border);
                    btnBookmark.setTag(0);
                    mDBHelper.removeBookmark(word);
                }
            }
        });
    }

    @Override
    public void onAttach(@NonNull Context context) {
        super.onAttach(context);
    }

    @Override
    public void onDetach() {
        super.onDetach();
    }
    
}

I’m trying to make a dictionary.

It’s just that the word details don’t work, I don’t know where the error is

dictionary fragment



import android.content.Context;
import android.os.Bundle;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;

import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.ListView;

import java.util.ArrayList;


public class DictionaryFragment extends Fragment {

    private String value = "Hello everyone!!!";
    private FragmentListener listener;
    ArrayAdapter<String> adapter;
    ListView dicList;
    private ArrayList<String> mSource = new ArrayList<String>();

    public DictionaryFragment() {
        // Required empty public constructor
    }


    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        // Inflate the layout for this fragment
        return inflater.inflate(R.layout.fragment_dictionary, container, false);
    }

    @Override
    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);

        dicList = view.findViewById(R.id.dictionaryList);
        adapter = new ArrayAdapter<String>(getContext(), android.R.layout.simple_list_item_1,mSource);
        dicList.setAdapter(adapter);
        dicList.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> adapterView, View view, int position, long l) {
                if (listener != null)
                    listener.onItemClick(mSource.get(position));
            }
        });
    }

    public void resetDataSource(ArrayList<String> source){
        mSource = source;
        adapter = new ArrayAdapter<String>(getContext(), android.R.layout.simple_list_item_1,mSource);
        dicList.setAdapter(adapter);
    }

    public void filterValue(String value){
        adapter.getFilter().filter(value);
        int size = adapter.getCount();
        for (int i = 0 ; i<size;i++){
            if (adapter.getItem(i).startsWith(value)){
                dicList.setSelection(i);
                break;
            }
        }
    }

    @Override
    public void onAttach(@NonNull Context context) {
        super.onAttach(context);
    }

    @Override
    public void onDetach() {
        super.onDetach();
    }

    public void setOnFragmentListener(FragmentListener listener){
        this.listener = listener;
    }
}

main activity

import androidx.appcompat.app.ActionBarDrawerToggle; import androidx.appcompat.app.AppCompatActivity; import androidx.appcompat.widget.Toolbar; import androidx.core.view.GravityCompat; import androidx.drawerlayout.widget.DrawerLayout; import androidx.fragment.app.Fragment; import androidx.fragment.app.FragmentManager; import androidx.fragment.app.FragmentTransaction; import androidx.lifecycle.ViewModel;

import android.os.Bundle; import android.text.Editable; import android.text.TextWatcher; import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.view.WindowManager; import android.widget.EditText;

import com.google.android.material.navigation.NavigationView;

import java.util.ArrayList;

public class kamusActivity extends AppCompatActivity implements NavigationView.OnNavigationItemSelectedListener {
MenuItem menuSetting;
Toolbar toolbar;

DBHelper dbHelper;

DictionaryFragment dictionaryFragment;
DetailFragment detailFragment;
BookmarkFragment bookmarkFragment;


@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_kamus);

    toolbar = (Toolbar) findViewById(R.id.toolbar);
    setSupportActionBar(toolbar);

    dbHelper = new DBHelper(this);

    DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
    ActionBarDrawerToggle toggle = new ActionBarDrawerToggle(
            this, drawer, toolbar, R.string.navigation_drawer_open, R.string.navigation_drawer_close);
    drawer.addDrawerListener(toggle);
    toggle.syncState();

    NavigationView navigationView = (NavigationView) findViewById(R.id.nav_view);
    navigationView.setNavigationItemSelectedListener((NavigationView.OnNavigationItemSelectedListener) this);

    dictionaryFragment = new DictionaryFragment();
    bookmarkFragment = BookmarkFragment.getNewInstance(dbHelper);
    goToFragment(dictionaryFragment, true);

    dictionaryFragment.setOnFragmentListener(new FragmentListener() {
        @Override
        public void onItemClick(String value) {
            String id = Bahasa.getState(kamusActivity.this,"dic_type");
            int dicType = id == null? R.id.action_it:Integer.valueOf(id);
            goToFragment( DetailFragment.getNewInstance(value,dbHelper,dicType),false);
        }
    });
    bookmarkFragment.setOnFragmentListener(new FragmentListener() {
        @Override
        public void onItemClick(String value) {
            String id = Bahasa.getState(kamusActivity.this,"dic_type");
            int dicType = id == null? R.id.action_it:Integer.valueOf(id);
            goToFragment( DetailFragment.getNewInstance(value,dbHelper,dicType),false);
        }
    });

    EditText edit_search = findViewById(R.id.edit_search);
    edit_search.addTextChangedListener(new TextWatcher() {
        @Override
        public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2){
            
        }
        
        @Override
        public void onTextChanged(CharSequence charSequence, int i, int i1, int i2){
            dictionaryFragment.filterValue(charSequence.toString());
        }
        
        @Override
        public void afterTextChanged(Editable editable){
           
        }
    });

    getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN);
}

@Override
public void onBackPressed() {
    DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
    if (drawer.isDrawerOpen(GravityCompat.START)) {
        drawer.closeDrawer(GravityCompat.START);
    }else {
        super.onBackPressed();
    }
}


@Override
public boolean onCreateOptionsMenu(Menu menu) {
    super.onCreateOptionsMenu(menu);
    // Inflate the menu; this adds items to the action bar if it is present
    getMenuInflater().inflate(R.menu.main_menu, menu);
    menuSetting = (MenuItem) menu.findItem(R.id.action_settings);

    String id = Bahasa.getState(this,"dic_type");
    if (id != null)
        onOptionsItemSelected(menu.findItem(Integer.valueOf(id)));
    else {
        ArrayList<String> source = dbHelper.getWord(R.id.action_it);
        dictionaryFragment.resetDataSource(source);
    }
    return true;
}

@Override
public boolean onOptionsItemSelected(MenuItem item) {
    int id = item.getItemId();

    //noinspection SimplifiableIfStatement
    if (id == R.id.action_it) {
        Bahasa.saveState(this,"dic_type", String.valueOf(id));
        ArrayList<String> source = dbHelper.getWord(id);
        dictionaryFragment.resetDataSource(source);
        menuSetting.setIcon(getDrawable(R.drawable.icon_bibt_2));
        return true;
    }else if (id == R.id.action_ti){
        Bahasa.saveState(this,"dic_type", String.valueOf(id));
        ArrayList<String> source = dbHelper.getWord(id);
        dictionaryFragment.resetDataSource(source);
        menuSetting.setIcon(getDrawable(R.drawable.icon_btbi_2));
        return true;
    }
       return super.onOptionsItemSelected(item);
}


public boolean onNavigationItemSelected(MenuItem item) {
    // Handle navigation view item clicks here.
    int id = item.getItemId();
    if (id == R.id.nav_bookmark){
        String activeFragment = getSupportFragmentManager().findFragmentById(R.id.fragment_container).getClass().getSimpleName();
        if(!activeFragment.equals(BookmarkFragment.class.getSimpleName())){
            goToFragment(bookmarkFragment, false);
        }
    }

    DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
    drawer.closeDrawer(GravityCompat.START);
    return true;
}

void goToFragment(Fragment fragment, boolean isTop){
    FragmentManager fragmentManager = getSupportFragmentManager();
    FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();

    fragmentTransaction.replace(R.id.fragment_container, fragment);
    if (!isTop)
        fragmentTransaction.addToBackStack(null);
    fragmentTransaction.commit();
}

@Override
public boolean onPrepareOptionsMenu(Menu menu) {
    String activeFragment = getSupportFragmentManager().findFragmentById(R.id.fragment_container).getClass().getSimpleName();
    if (activeFragment.equals(BookmarkFragment.class.getSimpleName())){
        menuSetting.setVisible(false);
        toolbar.findViewById(R.id.edit_search).setVisibility(View.GONE);
        toolbar.setTitle("Bookmark");
    }else {
        menuSetting.setVisible(true);
        toolbar.findViewById(R.id.edit_search).setVisibility(View.VISIBLE);
        toolbar.setTitle("");
    }
    return true;
}

}`

How to make my div clickable only once in react Tic Tac Toe game?

I am trying to mae a tic tac toe game using react. But I am unable to achieve the logic of button being clicable only once. Need some help. Here is the code:

import React, { useRef, useState } from 'react'


let data = ["","","","","","","","",""]



const Board = () => {

    let [count, setCount] = useState (0)
    const [allow,  setAllow] = useState(false)
    const [clicked, setClicked] = useState(false)

    const title = useRef("")


    const toggler  = (e, index) =>{
        if (allow){
            return 0
        }
        if (count % 2 === 0){
            e.target.innerHTML="<h1>X</h1>"
            data[index]="x"
            setCount(++count)
           
        }
        else{
            e.target.innerHTML="<h1>O</h1>"
            data[index]="o"
            setCount(++count)
            
        }

        if (index === 0){
            setClicked(true)
        }
        else if (index === 2){
            setClicked(true)
        }




        
        
        if (data[0]===data[1] && data[1]===data[2] && data[2]!==""){
            won(data[2])
         }
         else if (data[3]===data[4] && data[4]===data[5] && data[5]!==""){
            won(data[5])
         }
         else if (data[6]===data[7] && data[7]===data[8] && data[8]!==""){
            won(data[8])
         }
         else if (data[0]===data[3] && data[3]===data[6] && data[6]!==""){
            won(data[6])
         }
         else if (data[1]===data[4] && data[4]===data[7] && data[7]!==""){
            won(data[7])
         }
         else if (data[2]===data[5] && data[5]===data[8] && data[8]!==""){
            won(data[8])
         }
         else if (data[0]===data[4] && data[4]===data[8] && data[8]!==""){
            won(data[8])
         }
         else if (data[2]===data[4] && data[4]===data[6] && data[6]!==""){
            won(data[6])
         }
    }

    const won=(winner)=>{
        setAllow(true)
        if (winner === "x"){
            title.current.innerHTML="Congrats X won"
        }
        else if (winner === "o"){
            title.current.innerHTML="Congrats O won"
        }
        else if (count===9){
            title.current.innerHTML="It's a draw"
        }

    }

    // const  checkWinner = () => {
        
    // }

  return (
    <>  
    <h1 ref={title}>Welcome to the Game</h1>

    <div className='column'>
        <div className="row" onClick={!clicked?((e)=>{toggler(e,0)}):null}></div>
        <div className="row" onClick={!clicked?((e)=>{toggler(e,1)}):null}></div>
        <div className="row" onClick={!clicked?((e)=>{toggler(e,2)}):null}></div>
    </div>
    <div className='column'>
        <div className="row" onClick={!clicked?((e)=>{toggler(e,3)}):null}></div>
        <div className="row" onClick={!clicked?((e)=>{toggler(e,4)}):null}></div>
        <div className="row" onClick={!clicked?((e)=>{toggler(e,5)}):null}></div>
    </div>
    <div className='column'>
        <div className="row" onClick={!clicked?((e)=>{toggler(e,6)}):null}></div>
        <div className="row" onClick={!clicked?((e)=>{toggler(e,7)}):null}></div>
        <div className="row" onClick={!clicked?((e)=>{toggler(e,8)}):null}></div>
    </div>

    </>

  )
}

export default Board

top-level await in a module prevents `onload` from firing

I’m seeing that the load event won’t fire if I await for an IndexedDB opening at the top level in an indirectly loaded module.

If I remove the await, the load handler is called as expected.
If I keep the await but replace the openDB call with a dummy promise (like Promise.resolve(42)), the load handler is called as expected.

What is going on, and how can I debug this?

index.html:

...
<script type="module" src="js/ui_index.js" type="javascript/module"></script>
...

(it also contains an importmap, so all the imports work correctly)

ui_index.js:

import * as db from "database";
...
window.addEventListener('load', () => console.log('onload'));
console.log('ui');

database.js:

import { openDB } from "lib/idb";

//         ↓↓↓↓↓ *** THIS AWAIT ***
const db = await openDB("my-database-name", 4, {

    upgrade(db, old_ver, new_ver, tx) {
        console.log(`db.upgrade ${old_ver}→${new_ver}`);
    }
}

console.log('db:', db);

The idb module above is a promises-based IndexedDB wrapper by Jake Archibald.

Console output with await:

db.upgrade 4→5
db: Proxy { <target>: IDBDatabase, <handler>: {…} }
ui

Console output without await:

db: Promise { <state>: "pending" }
ui
onload
db.upgrade 5→6

(The db.upgrade line, of course, is only present if I increment the DB version in the openDB call. It does not affect the onload behavior).

I am testing this in Firefox 120 and have not tried other browsers; I’m building a small web app for in-house use which will only be used with Firefox.

How can I expand and collapse table rows (all of them, not each one) while the newest row always stays visible in pure js?

I have an input field where i put data in.

that data goes to a table and creates a new row, with the data in it.

the newest row is always on the top.

I want the table to show only the newest row and hide all the rest (meaning showing only one row),
and when i mouseover it using an event listener I want it to expand and show all other rows that were hidden.
when I mouseout, I want the table to collapse again and show only the newest row while hiding all the rest.

how can i do that?
every time i try to fix this i just make it worst
right now i doesnt show new rows at all for some reason.

thanks in advance!

thats my code:

HTML:

<body>
   <div class="container">
    <table id="myTable">
      <thead>
       <tr>
        <th>Name</th>
        <th>Number</th>
       </tr>
    </thead>
    <tbody>

    </tbody>
    </table>
 
    <form>
      <label for="fName">Name</label><br />
      <input type="text" id="fName" name="fName" /><br />
      <input type="submit" id="submitBtn" />
    </form>
  </div>

CSS:

table {
  color: black;
  width: 200px;
  border-collapse: collapse; 
  left: 100px;
  z-index: 1;
  position: absolute;
  box-shadow: 0px 2px 5px black;
}

.expand{
display: table-row !important;
}

 .collapse{ 
display: none;
}

 tr:not(:first-child){
display: none;
}

JS:

"use strict";

let inputBox = document.querySelector("#fName");
let tBody = document.querySelector("tbody");
const table = document.querySelector('table');

function addRow() {

  let row = tBody.insertRow(0);
  let cell1 = row.insertCell(0);
  let cell2 = row.insertCell(1);
  cell1.innerHTML = inputBox.value;
  cell2.innerHTML = "text2";
  inputBox.value = " ";
  row.classList.add('tr');
  
}

tBody.classList.add('collapse');

document.querySelector("#submitBtn").addEventListener("click", function (event) {
    event.preventDefault();
    addRow();
  });

  // collapst/expand event listeners:
  
  table.addEventListener('mouseover', function(){
    tBody.classList.remove('collapse');
    tBody.classList.add('expand');
  })

  table.addEventListener('mouseout', function(){
    tBody.classList.remove('expand');
    tBody.classList.add('collapse');
  })

How to listen to Azure Notifications Hub events in browser?

I have encountered some issues with Azure Notifications Hub with browser.
I have configured the VAPID Keys for my application.
I installed my browser using the API mentioned in this Microsoft Learn article.

To provide some context, I have already configured the VAPID Keys and installed my browser using the API mentioned here:

And installed my browser using this API:
https://learn.microsoft.com/en-us/rest/api/notificationhubs/create-registration
with this body:

<?xml version="1.0" encoding="utf-8"?>
<entry xmlns="http://www.w3.org/2005/Atom">    
    <content type="application/xml">        
        <BrowserRegistrationDescription xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.microsoft.com/netservices/2010/10/servicebus/connect">  
            <Endpoint>https://fcm.googleapis.com/fcm/send/.....</Endpoint>            
            <P256DH>{P256DH-value}</P256DH>            
            <Auth>{Auth-value}</Auth>        
        </BrowserRegistrationDescription>    
    </content>
</entry>

Additionally, I attempted to register my browser using JavaScript with the following code:

navigator.serviceWorker.register("service-worker.js");
Notification.requestPermission().then((permission) => {                 
    if (permission === 'granted') {                     
        // get service worker                     
        navigator.serviceWorker.ready.then((sw) => {                         
            // subscribe                         
            sw.pushManager.subscribe({                             
                userVisibleOnly: true,                             
                applicationServerKey: "VAPID_PUBLIC_KEY"
            }).then((subscription) => {                             
                let data = JSON.parse(JSON.stringify(subscription));       
                console.log(data);                                              
            });                     
        });                 
    }             
});

In the service-worker.js file, I have included the following code to handle push events:

self.addEventListener("push", (event) => {
    console.log(event);
    console.log(event.data);
    const notification = event.data.json();
    event.waitUntil(self.registration.showNotification(notification.title, {
        body: notification.body,
        icon: "icon.png",
        data: {
            notifURL: notification.url
        }
    }));
});

Despite my efforts, I am not receiving any events, and the test sends always fail. I kindly request your assistance in troubleshooting and resolving these issues.

login using aws-amplify library in react native

When i try to implement login functionality using aws-amplify library in react native. At that time we caught the following error like
AuthPiece.js:1 Uncaught TypeError: _awsAmplify.Logger is not a constructor
at ./node_modules/aws-amplify-react-native/dist/Auth/AuthPiece.js (AuthPiece.js:1:1)
at webpack_require (bootstrap:24:1)
at fn (hot module replacement:62:1)
at ./node_modules/aws-amplify-react-native/dist/Auth/Loading.js (Loading.js:1:1)
at webpack_require (bootstrap:24:1)
at fn (hot module replacement:62:1)
at ./node_modules/aws-amplify-react-native/dist/Auth/Authenticator.js (Authenticator.js:1:1)
at webpack_require (bootstrap:24:1)
at fn (hot module replacement:62:1)
at ./node_modules/aws-amplify-react-native/dist/Auth/index.js (index.js:1:1)

I would be tried following code

*** App.js file***

import { StatusBar } from 'expo-status-bar';
import React from 'react';
import { StyleSheet, Text, View } from 'react-native';
import { Amplify } from 'aws-amplify';
import  {withAuthenticator}  from 'aws-amplify-react-native';
import { config } from './aws-exports';

Amplify.configure(config);
export default function App() {
  return (
    <View style={styles.container}>
      <Text>Open up App.js to start working on your app!</Text>
      <StatusBar style="auto" />
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#fff',
    alignItems: 'center',
    justifyContent: 'center',
  },
});

*** package.json ***

{
  "name": "shop-with-login",
  "version": "1.0.0",
  "main": "index.js",
  "scripts": {
    "start": "expo start --dev-client",
    "android": "expo run:android",
    "ios": "expo run:ios",
    "web": "expo start --web"
  },
  "dependencies": {
    "@aws-amplify/ui-react-native": "^2.0.6",
    "aws-amplify": "^6.0.7",
    "aws-amplify-react-native": "^6.0.5",
    "expo": "~49.0.15",
    "expo-splash-screen": "~0.20.5",
    "expo-status-bar": "~1.6.0",
    "react": "18.2.0",
    "react-dom": "^18.2.0",
    "react-native": "0.72.6",
    "react-native-web": "~0.19.6"
  },
  "devDependencies": {
    "@babel/core": "^7.20.0"
  },
  "private": true
}

Better tree structure

I have this code:

class Category {
  constructor(
     readonly _title: string,
  ) { }

  get title() {
    return this._title
  }
}

const categories = {
  get pets() {
    const pets = new Category('Pets')

    return {
      get dishes() {
        const dishes = new Category('Dishes')

        return dishes
      }
    }
  },
  get cloth() {
    const cloth = new Category('Cloth')

    return {
      get accessories() {
        const accessories = new Category('Accessories')

        return accessories
      }
    }
  }
}

I want to use it like this

const product = new Product(categories.pets.dishes)

Its pretty good, but on frontend I should render this like tree:

<ul>
  {Object.entries(categories).map(
    ([slug, category]) => {
      return (
        <React.Fragment key={slug}>
          <li>{category.title}</li> // problem here
            <ul>
              {Object.entries(category).map(([s, c]) => {
                return <li key={s}>{c.title}</li>
              })}
            </ul>
         </React.Fragment>
       )
  })}
</ul>

How can I get the name of the parent category? What’s the best way to design this?

Create new object dynamically

Is there a way of adding an object without having to declare a const?

function Car (make, color) {
    this.make = make,
    this.color = color,  
    this.greet = function () {
        console.log(`The make is ${this.make} and color is ${this.color}`);
  };
}

const newObject = "Toyota"

const ford = new Car("Ford", "blue")
ford.greet();``

const vauxhall = new Car("Vauxhall", "green")
vauxhall.greet();

I thought I may be able to have:

const newObject = "Toyota"

Then add Toyota in place of ford or vauxhall.