AM Charts 5: Export menu always showing, toggle not working

I have created a world chart map that takes in heat data and displays it. I have also created an export and annotation menu based on the AM Charts documentation

JS:

        // Create root
        var root = am5.Root.new("mainHeatMap");

        // Set themes
        root.setThemes([
            am5themes_Animated.new(root)
        ]);

        // Create chart
        var chart = root.container.children.push(
            am5map.MapChart.new(root, {
                layout: root.horizontalLayout,
                projection: am5map.geoEqualEarth()
            })
        );

        // Create polygon series
        var polygonSeries = chart.series.push(
            am5map.MapPolygonSeries.new(root, {
                geoJSON: am5geodata_worldLow,
                valueField: "value",
                calculateAggregates: true,
                exclude: ["AQ"],
            })
        );

        // Set series world map to grey
        polygonSeries.set("fill", am5.color("#999999"));

        polygonSeries.mapPolygons.template.setAll({
            tooltipText: "{cname}, {value} trips - {description}"
        });

        polygonSeries.set("heatRules", [{
            target: polygonSeries.mapPolygons.template,
            dataField: "value",
            min: am5.color("#67b7dc"),
            max: am5.color("#00338d"),
            key: "fill"
        }]);

        polygonSeries.mapPolygons.template.events.on("click", function (ev) {
            HandleMapClick(ev.target)
        });

        polygonSeries.data.setAll(heatmapData);

        polygonSeries.mapPolygons.template.adapters.add("tooltipText", (text, target) => {
            const country = target.dataItem.dataContext.cname;
            if (country && heatmapData.some(data => data.country === country.id)) {
                return text;
            }
            return "";
        });

        var heatLegend = chart.children.push(
            am5.HeatLegend.new(root, {
                orientation: "horizontal",
                startColor: am5.color("#67b7dc"),
                endColor: am5.color("#00338d"),
                startText: String(min),
                endText: String(max),
                stepCount: 10,
                width: am5.percent(50),
                y: am5.percent(100),
                centerY: am5.percent(100)
            })
        );

        heatLegend.startLabel.setAll({
            fontSize: 12,
            fill: heatLegend.get("startColor")
        });

        heatLegend.endLabel.setAll({
            fontSize: 12,
            fill: heatLegend.get("endColor")
        });

        polygonSeries.events.on("datavalidated", function () {
            heatLegend.set("startValue", polygonSeries.getPrivate("valueLow"));
            heatLegend.set("endValue", polygonSeries.getPrivate("valueHigh"));
        });

        // Set up export and annotation
        var exporting = am5plugins_exporting.Exporting.new(root, {
            menu: am5plugins_exporting.ExportingMenu.new(root, {})
        });

        var annotator = am5plugins_exporting.Annotator.new(root, {});

        var menuitems = exporting.get("menu").get("items");

        menuitems.push({
            type: "separator"
        });

        menuitems.push({
            type: "custom",
            label: "Annotate",
            callback: function () {
                this.close();
                annotator.toggle();            
            }
        });

HTML:

    <script src="//cdn.amcharts.com/lib/5/index.js"></script>
    <script src="//cdn.amcharts.com/lib/5/map.js"></script>
    <script src="//cdn.amcharts.com/lib/5/geodata/worldLow.js"></script>
    <script src="//cdn.amcharts.com/lib/5/plugins/exporting.js"></script>
    <script src="//cdn.amcharts.com/lib/5/themes/Animated.js"></script>

With this code I get this output where my export/annotation menu is constantly showing and the toggle does not work:

World heatmap with export/annotation menu

Is there something I am missing here, maybe with initialising the menu as a child of the root? All of the functionality of the menu works as intended such as downloading images and annotations. The toggle does not work and the menu is always showing, not sure of a way around this.

I tried using an onClick event to trigger something in the console, but that was not working either. Another workaround was hiding the toggle icon, but it does not look good and takes up alot of room on the chart

Did you mean to import ../mongoData.js

[nodemon] starting node server.js
node:internal/process/esm_loader:97
internalBinding(‘errors’).triggerUncaughtException(
^

Error [ERR_MODULE_NOT_FOUND]: Cannot find module ‘/home/maitreya_10/CODE/WAD/discord-yt/studybud/server/mongoData’ imported from /home/maitreya_10/CODE/WAD/discord-yt/studybud/server/server.js
Did you mean to import ../mongoData.js?
at new NodeError (node:internal/errors:399:5)
at finalizeResolution (node:internal/modules/esm/resolve:326:11)
at moduleResolve (node:internal/modules/esm/resolve:945:10)
at defaultResolve (node:internal/modules/esm/resolve:1153:11)
at nextResolve (node:internal/modules/esm/loader:163:28)
at ESMLoader.resolve (node:internal/modules/esm/loader:838:30)
at ESMLoader.getModuleJob (node:internal/modules/esm/loader:424:18)
at ModuleWrap. (node:internal/modules/esm/module_job:77:40)
at link (node:internal/modules/esm/module_job:76:36) {
code: ‘ERR_MODULE_NOT_FOUND’
}

Node.js v18.15.0
[nodemon] app crashed – waiting for file changes before starting…

I’m trying to test the api endpoints for my application but i’m getting this error, anyone has any idea about it?

Can’t add onkeydown attribute to dynamically created input

HTML:

<form id="form">
  <div class="form-group" id="df2">
     <input type="text" class="form-control" name="atribut[]" onkeydown="changeIt(event)">
  </div>
  <div class="form-group" id="inputs">
  </div>
</form>

changeIt function:

<script>
let div = document.getElementById("df2");
let inputs = document.getElementById("inputs");
function changeIt(event) {
  var key = event.which || event.keyCode;
  if (key == '13') {
    const newInput = document.createElement('input');
    newInput.type = 'text';
    newInput.name = 'atribut[]';
    newInput.onkeydown = 'changeIt(event)';
    newInput.class = 'form-control';
    inputs.appendChild(newInput);
    console.log(newInput);
  }
}
</script>

And the console only showing type & name attributes of the new input. This is makes the new input can’t produce new inputs again (it stops resulting only 2 inputs created, the original and the new one). How to resovle this?

Why doesn’t react firestore create a document?

So I’m trying to create a new document containing these inputs, the console logs the right values for these and I don’t get any errors, only the one that I set in the function telling me that the document was not created

import React, { useRef, useState } from 'react'
import { Button, Card, Alert, Form } from 'react-bootstrap'
import { db } from '../firebase'
import { Timestamp, doc, setDoc } from 'firebase/firestore'

function Compose() {

    const emailRef = useRef('')
    const titleRef = useRef('')
    const textRef = useRef('')
    const [error, setError] = useState('')

    const data = {
        recipient: emailRef.current.value,
        title: titleRef.current.value,
        text: textRef.current.value,
        createdAt: Timestamp.fromDate(new Date())
    }

    async function handleSubmit(e) {
        e.preventDefault()

        try {
            setError('')
            await setDoc(doc(db, 'mails'), data)
        }
         catch {
            setError('Failed to send email!')
         }   
    }

  return (
    <>
        <h2>Compose</h2>
        <Card>
            <Card.Body>
                {error && <Alert variant='danger'>{error}</Alert>}
                <Form onSubmit={handleSubmit}>
                    <Form.Group id='email' className='mb-4'>
                        <Form.Label>To:</Form.Label>
                        <Form.Control type='email' ref={emailRef} required className='w-50' placeholder='The recipient'/>
                    </Form.Group>
                    <Form.Group id='title' className='mb-4'>
                        <Form.Label>Title:</Form.Label>
                        <Form.Control type='text' ref={titleRef} required className='w-50' placeholder='Subject'/>
                    </Form.Group>
                    <Form.Group id='body'>
                        <Form.Label>Text:</Form.Label>  
                        <Form.Control as='textarea' ref={textRef} rows='15' required/>
                    </Form.Group>
                    <Button type='submit' className='w-40 mt-4' style={{float:'right'}}>Send</Button>
                </Form>
            </Card.Body>
        </Card>
    </>
  )
}

export default Compose

db is the getFirestore(app)

I’ve tried just creating a document with fixed information on the submit of the form but it still does not work

React Native – UI freezes during API calls

I’m facing an issue in react native application where the UI freezes if any network requests are in progress. Every time I do a request to API the UI freezes until the request is responded.

React native version: 0.70.6

I’m calling 5 APIs in a screen, and there are some buttons in the screen which has different functionalities like opening the drawer navigator, navigate to other screen etc. The problem is that these button won’t respond if we click until all the 5 APIs return response.

I’m calling the APIs in useEffect (initial load)

useEffect(()=>{
    getCollaborations();
    getCreatorXP();
    getDeals(0);
    getTrending();
    getProfile();
},[])

Issue placing a marker in the correct spot on an OpenLayers 7 map

I am creating a webapp that allows user to create mulitple maps. On each map the user has the ability to add/remove a marker. They are then able to drag it into the appropriate location and/or change the size of the marker. For the most part the functionality is working but I am having a small issues.

When the user switches back and fourth between maps the app should reference the lat/lon of the marker (held in a variable) and places it back on the map. However when I switch to a previous map with a marker the marker location is slightly off. I have a feeling this has something to do with the anchor values of the marker but can’t quite figure out how to solve the problem. Any help is greatly appreciated.

To replicate this issue I take the following steps:

  1. Click on the first map text box, type/select an address to populate the map.
  2. Click the ‘Include a marker on this map’ checkbox to add a marker.
  3. Drag the marker to a new location on the map.
  4. Click on the second map tab (map should be hidden as no address is selected for this map)
  5. Click back to the first map tab.
  6. Marker is no longer in the correct place. It appears slightly above where it should on the map.

Here is a link to a JSFiddle with my current code.

/*jshint multistr: true */

/*these variables will come from previous user elections */
var numMaps = 2;
var selectedMapStyle = "OSMHumanitarian" //this will be selected on previous step
/* end user variables */

var currentMap;
var mapDetails = [];
var layoutDetails = [];
var tmpMapData = [];

// set some variables
var currentLonLat = [-63.5859487, 44.648618]; //Halifax Lon/Lat
//var currentLonLat = [-63.61089784934739,44.67050745]; //Leeds St. Lon/Lat
var defaultZoom = 12;
var defaultRotation = 0;

mapCenter = (ol.proj.fromLonLat(currentLonLat)); //Converts Lon/Lat to center

//array with available markers
var availableMarkers = [
  ['https://testing.52weekdog.com/images/markers/marker_marker01_black.svg', 0.06, [0.5, 1], 'standard'],
  ['https://testing.52weekdog.com/images/markers/marker_marker02_black.svg', 1.1, [0.5, 1], 'minimal'],
  ['https://testing.52weekdog.com/images/markers/marker_house01_black.svg', 0.8, [0.5, 1], 'house'],
  ['https://testing.52weekdog.com/images/markers/marker_pin01_black.svg', 1, [0.5, 1], 'pin']
];





//setup the map
const marker = new ol.Feature({
  geometry: new ol.geom.Point(ol.proj.fromLonLat([-63.5859487, 44.648618])),
});
const markerStyle = new ol.style.Style({
  image: new ol.style.Icon({
    anchor: availableMarkers[0][2],
        anchorYUnits: 'pixels',
    scale: availableMarkers[0][1],
    src: availableMarkers[0][0]
  })
});
const vectorSource = new ol.layer.Vector({
  source: new ol.source.Vector({
    features: [marker]
  }),
  style: markerStyle
});
marker.setStyle([markerStyle]);


const map = new ol.Map({
  view: new ol.View({
    center: mapCenter,
    zoom: defaultZoom,
    rotation: defaultRotation,
  }),
  target: 'map',
  controls: []
});
var view = map.getView();

var translate = new ol.interaction.Translate({
  features: new ol.Collection([marker])
});

map.addInteraction(translate);

translate.on('translating', function(evt) {
  var lonlat = ol.proj.transform(evt.coordinate, 'EPSG:3857', 'EPSG:4326');
  tmpMapData[currentMap][5] = lonlat;
  document.getElementById("markerDetails" + currentMap + "_lonlat").innerHTML = "[" + lonlat[0].toFixed(5) + ", " + lonlat[1].toFixed(5) + "]";
  checkMarkerLocation();
});

//setup the map controls
const rotationbutton = new ol.control.Rotate({
  target: 'rotateButton',
  autoHide: false
});

const zoombuttons = new ol.control.Zoom({
  target: 'zoom'
})
const zoomslider = new ol.control.ZoomSlider();
zoomslider.setTarget('zoom');
const attribution = new ol.control.Attribution({
  className: 'ol-attribution customAttribution',
  collapsed: false,
  collapsible: false,
  target: document.getElementById('attribution')
});

map.addControl(zoomslider);
map.addControl(zoombuttons);
map.addControl(rotationbutton);
map.addControl(attribution);

// when rotating using slider
rotationSlider.oninput = function() {
  map.getView().setRotation(degreesToRads(this.value - 180));
}

// update the rotation
map.getView().on("change:rotation", function(event) {
  deg = radsToDegrees(event.target.getRotation());
  if (deg < -180) {
    deg = deg + 360
  } else if (deg > 180) {
    deg = deg - 360;
  }
  document.getElementById('rotationSlider').value = deg + 180;

  updateMapDetails();


});
// END ROTATION //

map.getView().on(['change:center', 'change:resolution', 'change:rotation'], function() {
  updateMapDetails();
});


/* Base Map Layer */
const openStreetMapStandard = new ol.layer.Tile({
  source: new ol.source.OSM(),
  visible: false,
  title: 'OSMStandard'
})
const openStreetMapHumanitarian = new ol.layer.Tile({
  source: new ol.source.OSM({
    url: 'https://{a-c}.tile.openstreetmap.fr/hot/{z}/{x}/{y}.png'
  }),
  visible: false,
  title: 'OSMHumanitarian'
})
const stamenToner = new ol.layer.Tile({
  source: new ol.source.XYZ({
    url: 'https://stamen-tiles.a.ssl.fastly.net/toner/{z}/{x}/{y}.png',
    attributions: 'Map tiles by <a href="http://stamen.com">Stamen Design</a>, 
                        under <a href="http://creativecommons.org/licenses/by/3.0">CC BY 3.0</a>. 
                        Data by <a href="http://openstreetmap.org">OpenStreetMap</a>, under <a href="http://www.openstreetmap.org/copyright">ODbL</a>.'
  }),
  visible: false,
  title: 'StamenToner'
})
const stamenTerrain = new ol.layer.Tile({
  source: new ol.source.XYZ({
    url: 'https://stamen-tiles.a.ssl.fastly.net/terrain/{z}/{x}/{y}.jpg',
    attributions: 'Map tiles by <a href="http://stamen.com">Stamen Design</a>, 
                        under <a href="http://creativecommons.org/licenses/by/3.0">CC BY 3.0</a>. 
                        Data by <a href="http://openstreetmap.org">OpenStreetMap</a>, under <a href="http://www.openstreetmap.org/copyright">ODbL</a>.'
  }),
  visible: false,
  title: 'StamenTerrain'
})
const stamenWaterColor = new ol.layer.Tile({
  source: new ol.source.XYZ({
    url: 'https://stamen-tiles.a.ssl.fastly.net/watercolor/{z}/{x}/{y}.jpg',
    attributions: 'Map tiles by <a href="http://stamen.com">Stamen Design</a>, 
                        under <a href="http://creativecommons.org/licenses/by/3.0">CC BY 3.0</a>. 
                        Data by <a href="http://openstreetmap.org">OpenStreetMap</a>, under <a href="http://creativecommons.org/licenses/by-sa/3.0">CC BY SA</a>.'
  }),
  visible: false,
  title: 'StamenWatercolor'
})

/* End Base Map Layer */

//Layer Group
const baseLayerGroup = new ol.layer.Group({
  layers: [
    openStreetMapStandard, openStreetMapHumanitarian, stamenToner, stamenTerrain, stamenWaterColor, OpenStreetMap_Mapnik
  ]
})

map.addLayer(baseLayerGroup);


//populate based on number of maps
addressesContainer = document.getElementById('addressPickerContainer').getElementsByClassName('addressPickerTabColumn')[0];
for (i = 1; i <= numMaps; i++) {

  html = "<div id='MapTab" + i + "' class='addressPickerTab' onclick='clickTab(this)''>";
  html += "<div>";
  html += "<label>Map " + i + "</label><span>Incomplete</span>";
  html += "<div class='autocomplete-container' id='autocomplete-container" + i + "'></div>";
  html += "</div>";
  html += "</div>";
  html += "<div class='addressPickerDataBlock'>";
  html += "<div class='mapDetails'>";
  html += "<h4>Details for Map " + i + "</h4>";
  html += "Address: <span class='mapInfo' id='mapDetails" + i + "_address1'></span><br>";
  html += "Lon:  <span class='mapInfo' id='mapDetails" + i + "_lon'> </span> Lat: <span class='mapInfo' id='mapDetails" + i + "_lat'></span><br>";
  html += "Zoom: <span class='mapInfo' id='mapDetails" + i + "_zoom'></span> Map Rotation: <span class='mapInfo' id='mapDetails" + i + "_rotation'></span><br>";
  html += "</div>";
  html += "<div>";
  html += "<label class='includeMarkerChk'>";
  html += "<input type='checkbox' class='includeMarkerChk' id='includeMarkerChk" + i + "' onclick='toggleMarker(this)' /> Include a marker on this map.</label>";

  html += "<div class='chkBoxArea'>";
  html += "<button type='button' id='resetMarker" + i + "_btn' class='resetMarkerBtn' onclick='resetMarker()'>reset marker</button>";

  html += "<select name='markerSelect' onchange='changeMarker(this)' id='markerSelect" + i + "'>";
  for (m = 0; m < availableMarkers.length; m++) {
    html += "<option value='" + m + "'>" + availableMarkers[m][3] + "</option>";
  }
  html += "</select><br>";
  html += "Marker Size: <input type='range' oninput='resizeMarker(this)' id='markerSize" + i + "' min='1' max='100' value='10' class='markerSlider' onchange='resizeMarker(this)'>";
  html += "<span class='mapInfo' id='markerDetails" + i + "_size'>10</span><br>";
  html += "Current Marker Location: <span class='mapInfo' id='markerDetails" + i + "_lonlat'></span><br>";
  html += "<span class='mapInfo markerVisible' id='markerDetails" + i + "_visible'>The location of the marker is not currently on the map.</span><br>";
  html += "</div>";
  html += "</div>";
  html += "<div style='margin-top: -10px'>";
 
  html += "<button type='button' id='saveMap" + i + "_btn' class='btn btn-primary btn-sm saveMapBtn'>Save Details</button>";
  html += "</div>";
  html += "</div>";
  addressesContainer.innerHTML += html;
}

tmpMapData.push(["formatted", "lat", "lon", "zoom", "rotation", "markerLocation", "markerStyle", "markerSize", "mapStyle"]);

for (i = 1; i <= numMaps; i++) {
  const j = i;
  addressAutocomplete(document.getElementById("autocomplete-container" + j), (data) => {
    if (data) {
      //THIS IS WHAT HAPPENS WHEN AN ADDRESS IS SELECTED
      tmp = [data.properties.lon, data.properties.lat];
      tmpArray = [data.properties.formatted, data.properties.lat, data.properties.lon, defaultZoom, defaultRotation,
        tmp, availableMarkers[0][0], "10", selectedMapStyle
      ];
      tmpMapData[j] = tmpArray;

      //updateMap();
      document.getElementById("MapTab" + currentMap).nextElementSibling.classList.add("active");

      document.getElementById('MapTab' + currentMap).children[0].children[1].innerHTML = "In Progress";
      document.getElementById('MapTab' + currentMap).children[0].children[1].style.color = "orange";
    } else {
            // when delete X is pressed on autocomplete
    }

  }, {
    placeholder: "Enter an address for map " + j
  });

}



function updateMapDetails() {
  var center = ol.proj.transform(view.getCenter(), 'EPSG:3857', 'EPSG:4326');
  var zoom = view.getZoom();
  var rotation = view.getRotation();

  document.getElementById('mapDetails' + currentMap + '_address1').innerHTML = tmpMapData[currentMap][0];

  document.getElementById('mapDetails' + currentMap + '_lat').innerHTML = center[1];
  document.getElementById('mapDetails' + currentMap + '_lon').innerHTML = center[0];
  document.getElementById('mapDetails' + currentMap + '_zoom').innerHTML = zoom;
  document.getElementById('mapDetails' + currentMap + '_rotation').innerHTML = radsToDegrees(rotation) + "u00B0";
  document.getElementById('markerDetails' + currentMap + '_lonlat').innerHTML = "[" + tmpMapData[currentMap][5][0].toFixed(5) + ", " + tmpMapData[currentMap][5][1].toFixed(5) + "]";
  document.getElementById('markerSize' + currentMap).value = tmpMapData[currentMap][7];

  tmpMapData[currentMap][1] = center[1];
  tmpMapData[currentMap][2] = center[0];
  tmpMapData[currentMap][3] = zoom;
  tmpMapData[currentMap][4] = radsToDegrees(rotation);

}

function clickTab(tab) {
  clickedTab = document.getElementById(tab.id);
  var tabNum = clickedTab.id.substring(clickedTab.id.indexOf("MapTab") + 6);
  currentMap = tabNum;
            
  //if map details already exist
  if (tmpMapData[tabNum]) {

    //hide the image
    document.getElementById("layoutImage").style.display = "none";
    //load the mapData
    tmp_lat = tmpMapData[tabNum][1];
    tmp_lon = tmpMapData[tabNum][2];
    tmp_center = ol.proj.fromLonLat([tmp_lon, tmp_lat]);
    map.removeLayer(vectorSource);
    
        changeMarker(document.getElementById('markerSelect' + tabNum));
    resizeMarker(document.getElementById('markerSize' + tabNum));
    toggleMarker(document.getElementById('includeMarkerChk' + tabNum));


    setMap(tmp_center, tmpMapData[tabNum][3], tmpMapData[tabNum][4]);
    updateMapDetails();     

  } else {

    //hide the map and show the layout
    document.getElementById("layoutImage").style.display = "flex";

  }

  //switch the mask svg path

  if (clickedTab.classList.contains('active')) { //if the clicked tab is already active
    if (document.activeElement.classList[0] != "addressAutocompleteInput") { //if the active element isn't the autocomplete dropdown then minimize
      clickedTab.classList.remove("active");
      clickedTab.nextElementSibling.classList.remove("active");
      document.getElementById("layoutImage").style.display = "flex";
      document.getElementById("layoutImage").focus();
    }
  } else { //if the clicked tab is not currently active

    for (i = 0; i < clickedTab.parentElement.children.length; i++) { //close any tabs that are 'active'
      if (clickedTab.parentElement.children[i].classList.contains('active')) {
        // minimize the tab and hide the details
        clickedTab.parentElement.children[i].classList.remove("active");
        clickedTab.parentElement.children[i].nextElementSibling.classList.remove("active");
      }
    }
    //set the clicked tab to active
    clickedTab.classList.add("active");
    if (tmpMapData[currentMap]) {
      clickedTab.nextElementSibling.classList.add("active");
    }
    document.getElementById("autocomplete-container" + currentMap + "-input").focus();

  }

}

function setMap(center, zoom, rotation) {
  map.getView().setCenter(center);
  map.getView().setZoom(zoom);
  map.getView().setRotation(degreesToRads(rotation));
  //should add marker to this?
}

function toggleMarker(input) {

  if (input.checked == true) {
    input.parentElement.nextElementSibling.style.display = "block";

    //if there is no marker location set then set it to the center of the map
    marker.getGeometry().setCoordinates((ol.proj.fromLonLat(tmpMapData[currentMap][5])));
    /* reset marker to center if the marker is not visible on map when markerchk is checked
    if (checkMarkerLocation() == true){
          marker.getGeometry().setCoordinates((ol.proj.fromLonLat([tmpMapData[currentMap][2], tmpMapData[currentMap][1]])));
    }
    */

    map.addLayer(vectorSource);
    checkMarkerLocation();

  } else {
    input.parentElement.nextElementSibling.style.display = "none";
    map.removeLayer(vectorSource);
  }


}

function changeMarker(input) {
  markerStyle.setImage(new ol.style.Icon({
    anchor: availableMarkers[input.value][2],
    scale: availableMarkers[input.value][1],
    src: availableMarkers[input.value][0]
  }));
  marker.changed();
  resizeMarker(document.getElementById('markerSize' + currentMap));
  tmpMapData[currentMap][6] = availableMarkers[input.value][0];

  //tmpMapData[currentMap][6] = availableMarkers[input.value][2];
  //tmpMapData[currentMap][7] = availableMarkers[input.value][1];
}

function resizeMarker(input) {
  newScale = input.value / 10 * availableMarkers[document.getElementById('markerSelect' + currentMap).value][1];
  markerStyle.getImage().setScale([parseFloat(newScale), parseFloat(newScale)]);
  document.getElementById('markerDetails' + currentMap + '_size').innerHTML = input.value;
    tmpMapData[currentMap][7] = input.value;
  marker.changed();
}

function resetMarker() {
  marker.getGeometry().setCoordinates((ol.proj.fromLonLat(ol.proj.transform(view.getCenter(), 'EPSG:3857', 'EPSG:4326'))));
  tmpMapData[currentMap][5] = ol.proj.transform(view.getCenter(), 'EPSG:3857', 'EPSG:4326');
  updateMapDetails();
  resizeMarker(document.getElementById('markerSize' + currentMap));
}

function degreesToRads(deg) {
  return (deg) * (Math.PI / 180);
}

function radsToDegrees(rad) {
  return (rad) / (Math.PI / 180);
}

function checkMarkerLocation() {
  const size = map.getSize();
  const tl = map.getCoordinateFromPixel([0, 0]);
  const tr = map.getCoordinateFromPixel([size[0], 0]);
  const bl = map.getCoordinateFromPixel([0, size[1]]);
  const br = map.getCoordinateFromPixel(size);
  const polygon = new ol.geom.Polygon([
    [tl, tr, br, bl, tl]
  ]);
  if (polygon.intersectsCoordinate(marker.getGeometry().getCoordinates())) {
    document.getElementById('markerDetails' + currentMap + '_visible').style.visibility = 'hidden';
    return false;
  } else {
    document.getElementById('markerDetails' + currentMap + '_visible').style.visibility = 'visible';
    return true;
  }
}





function addressAutocomplete(containerElement, callback, options) {
  // create input element
  var inputElement = document.createElement("input");
  inputElement.setAttribute("type", "text");
  inputElement.setAttribute("class", "addressAutocompleteInput");
  inputElement.setAttribute("id", containerElement.id + "-input");
  inputElement.setAttribute("placeholder", options.placeholder);

  containerElement.appendChild(inputElement);

  // add input field clear button
  var clearButton = document.createElement("div");
  clearButton.classList.add("clear-button");
  addIcon(clearButton);

  //when a clear button is clicked
  clearButton.addEventListener("click", (e) => {
    //clear the currently selected address
    e.stopPropagation();
    inputElement.value = '';
    callback(null);
    clearButton.classList.remove("visible");
    document.getElementById("autocomplete-container" + currentMap + "-input").focus();
    document.getElementById('MapTab' + currentMap).children[0].children[1].style.color = "red";
    closeDropDownList();
  });
  containerElement.appendChild(clearButton);

  /* Current autocomplete items data (GeoJSON.Feature) */
  var currentItems;

  /* Active request promise reject function. To be able to cancel the promise when a new request comes */
  var currentPromiseReject;

  /* Focused item in the autocomplete list. This variable is used to navigate with buttons */
  var focusedItemIndex;

  /* Execute a function when someone writes in the text field: */
  inputElement.addEventListener("input", function(e) {
    var currentValue = this.value;
    var currentInput = this.id;


    /* Close any already open dropdown list */
    closeDropDownList();

    // Cancel previous request promise
    if (currentPromiseReject) {
      currentPromiseReject({
        canceled: true
      });
    }

    if (!currentValue) {
      clearButton.classList.remove("visible");
      return false;
    }

    // Show clearButton when there is a text
    clearButton.classList.add("visible");

    /* Create a new promise and send geocoding request */
    var promise = new Promise((resolve, reject) => {
      currentPromiseReject = reject;

      var apiKey = "47f523a46b944b47862e39509a7833a9";
      var url = `https://api.geoapify.com/v1/geocode/autocomplete?text=${encodeURIComponent(currentValue)}&limit=5&apiKey=${apiKey}`;

      if (options.type) {
        url += `&type=${options.type}`;
      }

      fetch(url)
        .then(response => {
          // check if the call was successful
          if (response.ok) {
            response.json().then(data => resolve(data));
          } else {
            response.json().then(data => reject(data));
          }
        });
    });

    promise.then((data) => {
      currentItems = data.features;

      /*create a DIV element that will contain the items (values):*/
      var autocompleteItemsElement = document.createElement("div");
      autocompleteItemsElement.setAttribute("class", "autocomplete-items");
      containerElement.appendChild(autocompleteItemsElement);

      /* For each item in the results */
      data.features.forEach((feature, index) => {
        /* Create a DIV element for each element: */
        var itemElement = document.createElement("DIV");
        /* Set formatted address as item value */
        itemElement.innerHTML = feature.properties.formatted;

        /* Set the value for the autocomplete text field and notify: */
        itemElement.addEventListener("click", function(e) {
          inputElement.value = currentItems[index].properties.formatted;

          //selecting address by clicking          
          callback(currentItems[index]);
          /* Close the list of autocompleted values: */
          closeDropDownList();
          /* give the focus back to the input field */
          document.getElementById(inputElement.id).focus();

          /* get the number of the input being seelected */
          /*
                    var inputNum = inputElement.id.substring(
            inputElement.id.indexOf("autocomplete-container") + 22,
            inputElement.id.lastIndexOf("-input")
          );
                    */
          //alert(currentMap + ' - ' + currentItems[index]);
          /* end */

        });

        autocompleteItemsElement.appendChild(itemElement);
      });
    }, (err) => {
      if (!err.canceled) {
        console.log(err);
      }
    });
  });

  /* Add support for keyboard navigation */
  inputElement.addEventListener("keydown", function(e) {
    var autocompleteItemsElement = containerElement.querySelector(".autocomplete-items");
    if (autocompleteItemsElement) {
      var itemElements = autocompleteItemsElement.getElementsByTagName("div");
      if (e.keyCode == 40) { //downarrow
        e.preventDefault();
        /*If the arrow DOWN key is pressed, increase the focusedItemIndex variable:*/
        focusedItemIndex = focusedItemIndex !== itemElements.length - 1 ? focusedItemIndex + 1 : 0;
        /*and and make the current item more visible:*/
        setActive(itemElements, focusedItemIndex);
        /* get the number of the input being seelected */
        /*
                var mySubString = inputElement.id.substring(
          inputElement.id.indexOf("autocomplete-container") + 22,
          inputElement.id.lastIndexOf("-input")
        );
                */
        //inputNum = mySubString.charCodeAt(0) - 64;
        //alert('input ' + inputNum + ' selected.');
        /* end */
      } else if (e.keyCode == 38) { //uparrow
        e.preventDefault();

        /*If the arrow UP key is pressed, decrease the focusedItemIndex variable:*/
        focusedItemIndex = focusedItemIndex !== 0 ? focusedItemIndex - 1 : focusedItemIndex = (itemElements.length - 1);
        /*and and make the current item more visible:*/
        setActive(itemElements, focusedItemIndex);
        /* get the number of the input being seelected */
        /*
             var mySubString = inputElement.id.substring(
          inputElement.id.indexOf("autocomplete-container") + 22,
          inputElement.id.lastIndexOf("-input")
        );
        inputNum = mySubString.charCodeAt(0) - 64;
                */
        //alert('input ' + inputNum + ' selected.');
        /* end */
      } else if (e.keyCode == 13) { //enter

        /* If the ENTER key is pressed and value as selected, close the list*/
        e.preventDefault();
        const itmNum = focusedItemIndex;

        if (focusedItemIndex > -1) {
          closeDropDownList();
        }

        document.getElementById("layoutImage").style.display = "none";

        setActive(itemElements, itmNum);
        tmp_center = ol.proj.fromLonLat([currentItems[itmNum].properties.lon, currentItems[itmNum].properties.lat]);
        setMap(tmp_center, defaultZoom, defaultRotation);

        /* get the number of the input being seelected */
        /*
                var mySubString = inputElement.id.substring(
          inputElement.id.indexOf("autocomplete-container") + 22,
          inputElement.id.lastIndexOf("-input")
        );
        inputNum = mySubString.charCodeAt(0) - 64;
                */
        //alert('input ' + inputNum + ' selected.');
        /* end */
      }
    } else {
      if (e.keyCode == 40) { //downarrow
        /* Open dropdown list again */
        var event = document.createEvent('Event');
        event.initEvent('input', true, true);
        inputElement.dispatchEvent(event);
      }
    }


  });

  // Used when scrolling autocomplete dropdown with keyboard
  function setActive(items, index) {
    if (!items || !items.length) return false;

    for (var i = 0; i < items.length; i++) {
      items[i].classList.remove("autocomplete-active");
    }
    /* Add class "autocomplete-active" to the active element*/
    items[index].classList.add("autocomplete-active");


    // Change input value and notify
    inputElement.value = currentItems[index].properties.formatted;

    callback(currentItems[index]);

  }

  function closeDropDownList() {
    var autocompleteItemsElement = containerElement.querySelector(".autocomplete-items");
    if (autocompleteItemsElement) {
      containerElement.removeChild(autocompleteItemsElement);
    }

    focusedItemIndex = -1;
  }

  function addIcon(buttonElement) {
    var svgElement = document.createElementNS("http://www.w3.org/2000/svg", 'svg');
    svgElement.setAttribute('viewBox', "0 0 24 24");
    svgElement.setAttribute('height', "24");

    var iconElement = document.createElementNS("http://www.w3.org/2000/svg", 'path');
    iconElement.setAttribute("d", "M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z");
    iconElement.setAttribute('fill', 'currentColor');
    svgElement.appendChild(iconElement);
    buttonElement.appendChild(svgElement);
  }

  /* Close the autocomplete dropdown when the document is clicked. 
    Skip, when a user clicks on the input field */
  document.addEventListener("click", function(e) {
    if (e.target !== inputElement) {
      closeDropDownList();
    } else if (!containerElement.querySelector(".autocomplete-items")) {
      // open dropdown list again
      var event = document.createEvent('Event');
      event.initEvent('input', true, true);
      inputElement.dispatchEvent(event);
    }
  });

}

Using puppeteer/cypress functionality in chrome extension app

I’m looking to build a small bot in a Chrome extension. The idea is to find and fill out forms on the current website.

Puppeteer/Cypress has all the tools I need for this, e.g. waiting for the selector to appear, timeouts, filling forms easily, etc.
I am not asking to install Puppeteer on Chrome extension or anything, just looking for a library that offers the utils I described.

Do you guys know if there’s tooling like this around? Thanks.

how can I make the click event be executed first before the focusout event is executed?

Basically in a real example of my code I have this problem:
when i start typing in a text field, it is shown a modal (in my case a div) and i want that when I click on this div, i want the click event to be executed.
The idea is that this div disappears when I click outside the text field.

I understand that first the @focusout is executed when I click outside of the input search, before the @click event is executed.

How can I fix this? in my web page I can have several other HTML elements and when the user clicks on any other element I need the @focusout to be executed when previously typed in the text field and not would be a good practice put in each element different to my input text a myInputText.blur() event for example.

new Vue({
  el: '#app',
  methods: {
    clickEvent() {
      console.log('click');
    },
    blurEvent() {
      console.log('focusout');
      this.myText="";
    },
  },
  data() {
    return {
      myText: ''
    };
  },
})
<div id="app">
    <div class="hello">
      <input  v-model="myText" @focusout="blurEvent" placeholder="write something">
      <div style="cursor:pointer;border:1px solid red; position:fixed; right:50%; top:0px;" v-if="myText"       @click="clickEvent">Select on option</div>
    </div>
</div>

<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.7.10/vue.min.js"></script>

How can i create website that generates documents in pdf? [closed]

Im making my portfolio to get the first job in javascript/frontend.

I would like to build a website that generates documents/applications after entering the appropriate data in the survey.

For example, the user enters his data such as: name, address, date of birth and selects the type of application he is interested in, e.g. application for dismissal.

The application is generated on the website in a visible way and is available for download. Without sending data to the server. Is it possible to do it directly on the website? Please give me some tips.

Thank you a lot!

So far I have found solutions related to fetch data from api. However, I’d like to do it directly on the page

Each child in a list should have a unique “key” prop warning in a reocurring component

Using the same component for a few different pages in my project, but on one specific page called “customersPage” it returns

Warning: Each child in a list should have a unique “key” prop.
Check the render method of DetailedRow. See https://reactjs.org/link/warning-keys for more information.
at div
at DetailedRow (http://localhost:3001/static/js/bundle.js:1816:5)
at tbody
at http://localhost:3001/static/js/bundle.js:4740:66
at TableBody (http://localhost:3001/static/js/bundle.js:26111:82)
at table
at http://localhost:3001/static/js/bundle.js:4740:66
at Table (http://localhost:3001/static/js/bundle.js:26979:82)
at div
at http://localhost:3001/static/js/bundle.js:4740:66
at Paper (http://localhost:3001/static/js/bundle.js:23349:83)
at http://localhost:3001/static/js/bundle.js:4740:66
at TableContainer (http://localhost:3001/static/js/bundle.js:26511:82)
at div
at TableComp (http://localhost:3001/static/js/bundle.js:2090:62)
at div
at CustomersPage (http://localhost:3001/static/js/bundle.js:505:77)

I’ve tried adding a unique key prop to the main div elements and the components but with no changes. Keep in mind that I use the same component on different pages and all are passing the same props, just with different values.

Here’s customersPage:

    const CustomersPage = () => {
      const storeData = useSelector(state => state);
      const [open, setOpen] = React.useState(false);
      const [success, setSuccess] = React.useState(false);
      
      React.useEffect(() => {
        customerData();   
      }, [storeData.purchases]);

      const customerData = () => {
        console.log(storeData.customers);
        const customers = storeData.customers.map(customer => {
          const purchases = storeData.purchases.filter(purchase=> purchase.customerId === customer.id);
          const customerWithPurchases = {
            ...customer,
            purchases: purchases.map(purchase => {
              const product = storeData.products.find(product => product.id === purchase.productId);
              return {
                ...purchase,
                product: product
              };
            })
          };
          return customerWithPurchases});
        return customers; 
      }
        
      const [data, setData] = React.useState(customerData);
      const handleDialog = () => {setOpen(!open);}
      
      React.useEffect(() => {
        if (success) {
          const timeoutId = setTimeout(() => {
            setSuccess(false);
          }, 3000);
          return () => {
            clearTimeout(timeoutId);
          }
        }
      }, [success]);
      return (
        <div key={data} style={{backgroundColor: '#0288d1', height: '600px'}}>
            <BuyProductDialog open={open} setSuccess={setSuccess} handleDialog={handleDialog}></BuyProductDialog>
            <br/><br/>
            <ColorButton variant="contained" size="large" onClick={handleDialog}>Buy Product</ColorButton>
            {success? (<Alert severity="success">This is a success alert — check it out!</Alert>) : ('')}
            <br/><br/>
            <TableComp context={'customers'} key={data} data={data}/>
        </div>
      )
    }

    const ColorButton = styled(Button)(({ theme }) => ({
      color: theme.palette.getContrastText(purple[500]),
      backgroundColor: purple[500],
      '&:hover': {
        backgroundColor: purple[700],
      },
    }));
    export default CustomersPage;

TableComp.js:

    export const TableComp = (props) => {
      const [rows, setRows] = React.useState([]);
      const [columns, setColumns] = React.useState([])
      const [data, setData] = React.useState(props.data);
      const storeData = useSelector(state => state);

      React.useEffect(() => {
      if (props.context === 'products') {
       setRows(storeData.products);
       setColumns(['Name', 'Price', 'Quantity']); 
      }
      else if (props.context === 'customers')
      {
        setRows(data);
        setColumns(['Name','Purchased Products','Purchase Dates'])
      }
      else if (props.context === 'purchases') {
        const purchases = storeData.purchases.filter(purchase => purchase.productId === props.product.id); 
        const customerIds = purchases.map(purchase => {return {id: purchase.customerId, date: purchase.date}});
        
        const customers = customerIds.map(customerId => {
          const customer = storeData.customers.find(customer => customer.id === customerId.id);
          return {...customer, date: customerId.date};
        });
        setRows(customers);
        setColumns(['Name', 'City', 'Date'])
      }
      else if (props.context === 'allPurchases') {
        setRows(data);
        setColumns(['Name', 'Product', 'Date'])
      }
      else if (props.context === 'editCustomers')
      {
        setRows(data.purchases);
        setColumns(['Name','Purchased Product','Purchase Date']);
      }  
      }, [props.context]);

      return (
        
        <div style={{height: '250px'}} key={rows}>
        <TableContainer component={Paper}>
          <Table aria-label="collapsible table">
            <TableHead>
              <TableRow>
                <TableCell />
                <TableCell>{columns[0]}</TableCell>
                <TableCell align="center">{columns[1]}</TableCell>
                <TableCell align="right">{columns[2]}&nbsp;</TableCell>
              </TableRow>
            </TableHead>
            <TableBody>
              {rows.map((row) => <DetailedRow key={row.id} context={props.context} row={row} />
              )}
            </TableBody>
          </Table>
        </TableContainer>
        </div>
      );
    }
    export default React.memo(TableComp);

DetailedRow.js:

    const DetailedRow = (props) => {
    const { row } = props;
    const storeData = useSelector(state => state);
    const location = useLocation();
    const locationContext = location.pathname || '';
    const [open, setOpen] = useState(false);
    const [cells, setCells] = useState([]);
    const [context, setContext] = useState('');

    useEffect(() => {checkContext();}, [row, props.context]);

        const checkContext = () => {
            if (props.context === 'products') {
                setCells([row.name, row.price, row.quantity]);
                setContext(props.context);
            }
            else if (props.context === 'purchases') {
                setCells([(row.fname + ' ' + row.lname), row.city, row.date]);
                setContext('customers');
            }
            else if (props.context === 'customers') {
                const products = row.purchases.map(purchase => purchase.product);
                const purchaseDates = row.purchases.map(purchase => purchase.date);
                setCells([(row.fname + ' ' + row.lname), products ,purchaseDates]);
                setContext('customers')
            }
            else if (props.context === 'allPurchases')
            {
                setCells([row.customerName, row.productName, row.date]);
                setContext('purchases');
            }
            else if (props.context === 'editCustomers')
            {const customer = storeData.customers.find(customer => customer.id === row.customerId)
                setCells([(customer.fname +" "+customer.lname), row.productName, row.purchaseDate])
            setContext('editCustomers')}
    }       
      return (
            <React.Fragment key={row.id}>
            <TableRow sx={{ '& > *': { borderBottom: 'unset' } }}>
                <TableCell>
                <IconButton aria-label="expand row"
                    size="small" onClick={() => setOpen(!open)}>
                    {props.context === 'products'? (open ? <KeyboardArrowUpIcon /> : <KeyboardArrowDownIcon />) : ''}
                </IconButton>
                </TableCell>

                <TableCell component="th" scope="row">
                {context === 'customers' ? (<Link key={row.id} to={`/customers/${row.id}`}>{cells[0]}</Link>) : 
                (context === 'purchases'? (<Link to={`/customers/${row.customerId}`}>{cells[0]}</Link>) : 
                (context === 'editCustomers'? (cells[0]) :
                (<Link to={`/${context}/${row.id}`}>{cells[0]}</Link>)))}
                </TableCell>

                <TableCell align="center">
                {context === 'customers' ? ( locationContext.includes('/products/')? (cells[1]) :
                 (cells[1].map(product => {
                 const productId = row.purchases.find(purchase => purchase.product.name === product.name)?.product.id;
                 return (<div key={row.id}><Link to={`/products/${productId}`} key={productId}>{product.name}</Link>, </div>);}))) :
                (context === 'purchases'? (<Link to={`/products/${row.productId}`}>{cells[1]}</Link>) :
                (context === 'editCustomers'? <Link to={`/products/${row.productId}`}>{cells[1]}</Link>:cells[1]))}
                </TableCell>

                <TableCell align="right">
                {context === 'editCustomers'? (cells[2]) : (cells[2] && locationContext.includes('/customer')? 
                (cells[2].map(date => {const tempDate = date.toString();
                    return(<div>{tempDate}, </div>)}))
                : (cells[2]))}
                </TableCell>
            </TableRow>
            <TableRow>
                <TableCell style={{ paddingBottom: 0, paddingTop: 0 }} colSpan={6}>
                <Collapse in={open} timeout="auto" unmountOnExit>
                    <PurchasedComp key={row.id} row={row} context={props.context}/>
                </Collapse>
                </TableCell>
            </TableRow>
            </React.Fragment>
        );
        }

    export default React.memo(DetailedRow);

Plane Displacement using a height map with ThreeJS is not working

the heightmap i wannt to use, but its not a must

my current scene with the posted code

import * as THREE from 'three';
import {OrbitControls} from 'three/examples/jsm/controls/OrbitControls.js';
import * as dat from 'dat.gui';

// dimensions of the plane x, y
const planeDim = new THREE.Vector2(20, 20);
const planeSeg = new THREE.Vector2(100, 100);

// Cam start coordinates x, y, z
let camPos = new THREE.Vector3(-10, 30, 30);

// Cam settings
const camFOV = 45;
const camAspect = window.innerWidth / window.innerHeight;
let camNear = 0.1;
let camFar = 1000;

// AxesHelper size
let axesHelperSize = 5;

// Gridhelper dimensions x, y
const gridHelperDim = new THREE.Vector2(20, 20);

// Mouseover highligthed tile starting coordinates x, y, z
let tilePos = new THREE.Vector3(0.5, 0, 0.5);

// Mouseover highlighted tile dimensions x, y
const tileDim = new THREE.Vector2(1, 1);


// Creating variables to work with raycasting from mouseposition
const mousePosition = new THREE.Vector2();
const raycaster = new THREE.Raycaster();
let intersects;

// Array of all sphere objects placed on the plane
const objects = [];

//creating the renderer
const renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);

// enable shadows in the scene
renderer.shadowMap.enabled = true;

document.body.appendChild(renderer.domElement);

// Creating the Scene
const scene = new THREE.Scene();

// Creating the camera
const camera = new THREE.PerspectiveCamera(
    camFOV,
    camAspect,
    camNear,
    camFar
);

// Declaring the Camera as an OrbitCamera
const orbit = new OrbitControls(camera, renderer.domElement);
camera.position.set(camPos.x, camPos.y, camPos.z);
orbit.update();

// creating and adding the AxesHelper to the scene
const axesHelper = new THREE.AxesHelper(axesHelperSize);
scene.add(axesHelper);


// Loading the heightmap-texture
const loader = new THREE.TextureLoader();
const displacementMap = loader.load('res/heightmap.jpg');

// creating the plane with displacement
const planeMesh = new THREE.Mesh(
  new THREE.PlaneGeometry(planeDim.x, planeDim.y, planeSeg.x, planeSeg.y),
  new THREE.MeshPhongMaterial({
    color: 0xFFFFFF,
    side: THREE.DoubleSide,
    visible: true,
    displacementMap: displacementMap,
    displacementScale: 20
  })
);
// enable recieving shadows on the plane
planeMesh.receiveShadow = true;

// giving the plane a name
planeMesh.name = 'ground';
// adding the plane to the scene
scene.add(planeMesh);
// rotate the plane 90 degrees
planeMesh.rotation.x = -Math.PI / 2;

// creating the gridHelper on the plane
const gridHelper = new THREE.GridHelper(gridHelperDim.x, gridHelperDim.y);
// adding the gridhelper into the scene
scene.add(gridHelper);

// creating the highlighted tile, setting its position and adding it to the scene
const highlightMesh = new THREE.Mesh(
  new THREE.PlaneGeometry(tileDim.x, tileDim.y),
  new THREE.MeshBasicMaterial({
    color: 0x00FF00,
    side: THREE.DoubleSide,
    transparent: true
  })
);
highlightMesh.position.set(tilePos.x, tilePos.y, tilePos.z);
highlightMesh.rotation.x = -Math.PI / 2;
scene.add(highlightMesh);

// raycasting function. Tile on mouseposition will be highlighted
window.addEventListener('mousemove', function(e){
  mousePosition.x = (e.clientX / this.window.innerWidth) * 2 - 1;
  mousePosition.y = -(e.clientY / this.window.innerHeight) * 2 + 1;
  raycaster.setFromCamera(mousePosition, camera);
  intersects = raycaster.intersectObjects(scene.children);
  intersects.forEach(function(intersect){
    if(intersect.object.name === 'ground'){
      const highlightPos = new THREE.Vector3().copy(intersect.point).floor().addScalar(0.5);
      highlightMesh.position.set(highlightPos.x, 0, highlightPos.z);
    
      // returns true if tilespace is already used
      const objectExists = objects.find(function(object){
        return (object.position.x === highlightMesh.position.x)
        && (object.position.z === highlightMesh.position.z);
      });

      // changes tile color to white if tile is empty and red if not
      if(!objectExists){
        highlightMesh.material.color.setHex(0x00FF00);
      }else{
        highlightMesh.material.color.setHex(0xFF0000);
      }
    }
  });
});

// Creating the sphere object
const sphereMesh = new THREE.Mesh(
  new THREE.SphereGeometry(0.4, 4, 2),
  new THREE.MeshStandardMaterial({
    wireframe: true,
    color: 0xAEAEDB
  })
);
// enabling sphere to cast a shadow
sphereMesh.castShadow = true;

// Click event, clicking tile will spawn the sphere object on it
window.addEventListener('mousedown', function(){
  // returns true if the clicked tile already has a sphere
  const objectExists = objects.find(function(object){
    return (object.position.x === highlightMesh.position.x)
    && (object.position.z === highlightMesh.position.z);
  })

  // if tile is empty, spawn a shpere, else - console log
  if(!objectExists){
    intersects.forEach(function(intersect){
      if(intersect.object.name === 'ground'){
        const sphereClone = sphereMesh.clone();
        sphereClone.position.copy(highlightMesh.position);
        scene.add(sphereClone);
        objects.push(sphereClone);
        //make tile red instantly after clicking to indicate the tile space is already in use
        highlightMesh.material.color.setHex(0xFF0000);
      }
    }) 
  }else{
    console.log('Can not place, space is already used!')
  }
});


// adding ambient light to the scene
const ambientLight = new THREE.AmbientLight(0x333333);
scene.add(ambientLight);

// adding a spotlight to the scene
const spotLight = new THREE.SpotLight(0xFFFFFF);
scene.add(spotLight);
spotLight.position.set(-100, 100, 0);
spotLight.castShadow = true;
spotLight.angle = 0.2;
// ading a lighthelper to help see the light settings
const sLightHelper = new THREE.SpotLightHelper(spotLight);
scene.add(sLightHelper);


// creating the light gui itself
const gui = new dat.GUI();

// adding options to dat.gui to change seetings of the light and plane
const options = {
  angle: 0.2,
  penumbra: 0,
  intensity: 1,
  wireframe: false
};
// creating the light gui settings and setting its boundries
gui.add(options, 'angle', 0, 1);
gui.add(options, 'penumbra', 0, 1);
gui.add(options, 'intensity', 0, 1);

// enables wireframemode for the plane
gui.add(options, 'wireframe').onChange(function(e){
  planeMesh.material.wireframe = e;
});

// animation loop with time parameter
function animate(time){
  // bind the gui options to the spotlight
  spotLight.angle = options.angle;
  spotLight.penumbra = options.penumbra;
  spotLight.intensity = options.intensity;
  // bind the gui options to the plane
  planeMesh.wireframe = options.wireframe;
  // update lighthelper appearence according to the settings
  sLightHelper.update();
  // make the tile blinking
  highlightMesh.material.opacity = 1 + Math.sin(time / 120);
  // rotation animation on every sphere object on the plane
  objects.forEach(function(object){
    object.rotation.x = time / 500;
    object.rotation.y = time / 500;
    object.position.y = 0.5 + 0.5 * Math.abs(Math.sin(time / 1000));
  })
  renderer.render(scene, camera);
}
renderer.setAnimationLoop(animate);

// logging in the browser console
console.log();

Hello,
sorry if my English is bad. I’m currently working on a small game project to learn JavaScript and ThreeJS. My Problem is, that my approach to displacements using height maps don’t work. I want to generate a Plane with a displaced surface, but it either don’t apply at all and the plane stays flat and white, or it still seems to not apply, but the plane turns black. I couldn’t find any working solution, so I hope you can help me. Don’t kill me for coding everything in one class, I will make sure to refactor the code in the future and follow object-oriented principles, but for now it’s fine.
Also its my first post here, so let me know what i should do better, to get better help from you. Also let me know if you need some additional information.

Thanks in advance and im looking forward to implement a fancy displaced map into my learning project game.

I looked many youtube tutorials, even did new projects and followed every line of code step by step, but its still not working. I tried to change the path of the heightmap.jpg but still same results. I also tried diffrent ThreeJS materials but with no success. I know im missing something but i’m out of ideas.