1

I have an SVG map of the world with elements made up of the following structure


  <path
     inkscape:connector-curvature="0"
     id="AF"
     data-name="Afghanistan"
     data-id="AF"
     d="m 1369.9,333.8 -5.4,0 -3.8,-0.5 -2.5,2.9 -2.1,0.7 -1.5,1.3 -2.6,-2.1 -1,-5.4 -1.6,-0.3 0,-2 -3.2,-1.5 -1.7,2.3 0.2,2.6 -0.6,0.9 -3.2,-0.1 -0.9,3 -2.1,-1.3 -3.3,2.1 -1.8,-0.8 -4.3,-1.4 -2.9,0 -1.6,-0.2 -2.9,-1.7 -0.3,2.3 -4.1,1.2 0.1,5.2 -2.5,2 -4,0.9 -0.4,3 -3.9,0.8 -5.9,-2.4 -0.5,8 -0.5,4.7 2.5,0.9 -1.6,3.5 2.7,5.1 1.1,4 4.3,1.1 1.1,4 -3.9,5.8 9.6,3.2 5.3,-0.9 3.3,0.8 0.9,-1.4 3.8,0.5 6.6,-2.6 -0.8,-5.4 2.3,-3.6 4,0 0.2,-1.7 4,-0.9 2.1,0.6 1.7,-1.8 -1.1,-3.8 1.5,-3.8 3,-1.6 -3,-4.2 5.1,0.2 0.9,-2.3 -0.8,-2.5 2,-2.7 -1.4,-3.2 -1.9,-2.8 2.4,-2.8 5.3,-1.3 5.8,-0.8 2.4,-1.2 2.8,-0.7 -1.4,-1.9 z"
     style="fill:#f2f2f2;fill-rule:evenodd" />

I'm trying to make it so that when I click on a country that is randomly generated, if the user correctly clicks on it first time, that country will turn white and another randomly generated country will be asked. (I need to try to get this to randomly work when a user clicks the correct country at the moment it only works when I refresh the page).

I have a function that will change the colour to white, however it doesn't depend on the question that is being asked. Developing that dependency is where I'm stuck.

function onClickHandler(elem){
   var country_id = elem.id
   var colour = "#ffffff";
   elem.style.fill = colour;
}

click function being used:

<path
     inkscape:connector-curvature="0"
     id="Algeria"
     onmouseover="displayName('Algeria')"
     onclick="onClickHandler(this)"

One possible solution I've thought of is, make some invisible textboxes on each piece of the map, and then just test to see if the array item matches the clicked on textbox? But i'm not sure how I could implement it in code. Maybe I can wrap each path tag inside another tag and target that type of tag?? Below is what I've done so far:

var items = Array("Afghanistan", "Algeria", "Australia", "Ecuador", "Bahrain", "Bhutan");

function shuffle(array){
   var currentIndex = array.length, temporaryValue, randomIndex;
   while(0 !== currentIndex){
      randomIndex = Math.floor(Math.random() * currentIndex);
      currentIndex -= 1;
      temporaryValue = array[currentIndex];
      array[currentIndex] = array[randomIndex];
      array[randomIndex] = temporaryValue;
   }

   return array;
}

function nextQuestion(){
   items = shuffle(items);
   objQ = items[0];
   return objQ;

}


var e; //event
var i; //index counter

// Aim to try and put hidden text box on each answer and if the array matches the clicked area highlight it white.

function checkAnswer(){
   for(i = 0; i < items.length; i++){
       if(document.getElementById('ansfield').value  == onClickHandler(this)){
         onClickHandler(this).fill = "white";    
// don't think this is correct. 
//Trying to get it to turn the country 
//with the matched name to the array white.  
//Probably need some sort of onclick event...
}
   }
}
3
  • I'm not sure what you mean with text boxes (I can't see anything like that in your code and, anyway, country shapes don't look like boxes) but SVG elements are scriptable. You can attach event handlers as you'd do with HTML. Commented Apr 25, 2020 at 11:21
  • I haven't added any textboxes but was thinking can I not add some somehow. Fairly new to all this so not sure. Hmm so maybe I can inside my if statement use the event handler? Commented Apr 25, 2020 at 11:23
  • Yes, you can certainly add <input> or <textarea> elements and use some carefully crafted CSS to position them on top of the map. But: 1) Text boxes are rectangular, countries aren't 2) The only thing text boxes allow and <path> elements don't is typing. Please note we don't have the context you do, you really need to explain how you'd like to use those text boxes. Commented Apr 25, 2020 at 11:59

1 Answer 1

1

This seems related to your other question?: Getting an SVG map to change color on click

So I take my answer from that question as a starting point and try to give a minimal reproducible example (https://stackoverflow.com/help/minimal-reproducible-example)

It would work smoother with more SVG paths, so if you supply those I can also add them.

In short what happens:

  • When you click the button, it picks a random country path and puts its name into the span in the h4 tag
  • When you click a country, if that country is the same as the one in the span in h4 tag then fill the country green, otherwise red.
  • When you click the button again, loop all country paths and reset their fills to the initial grey

const targetSpan = document.querySelector('#target')
const allPaths = document.querySelectorAll('svg > path');

function displayName(elem){
  console.log(elem.id)
}

allPaths.forEach(elem => elem.addEventListener('click', myFuncHandler));

function myFuncHandler(e){
  const country_id = e.target.id
  const thisCountry = e.target.getAttribute('data-name');
  const colour = thisCountry == targetSpan.innerHTML ? "#004400" : "#8a0000";
  e.target.style.fill = colour;
}

const startButton = document.querySelector('#startButton');
startButton.addEventListener('click', e => {
  const randIndex = Math.floor(Math.random() * (allPaths.length));
  const randCountry = allPaths[randIndex];
  targetSpan.innerHTML = randCountry.getAttribute('data-name');
  allPaths.forEach(elem => elem.style.fill = "#f2f2f2");
});
<button id="startButton">Start Game</button>
<h4>Please click on <span id="target"></span></h4>

<svg viewbox="250 250 1250 1000">
<path
     inkscape:connector-curvature="0"
     id="Algeria"
     onmouseover="displayName(this)"
     data-name="Algeria"
     data-id="DZ"
     d="m 1021,336.9 -3.6,0.4 -2.2,-1.5 -5.6,0 -4.9,2.6 -2.7,-1 -8.7,0.5 -8.9,1.2 -5,2 -3.4,2.6 -5.7,1.2 -5.1,3.5 2,4.1 0.3,3.9 1.8,6.7 1.4,1.4 -1,2.5 -7,1 -2.5,2.4 -3.1,0.5 -0.3,4.7 -6.3,2.5 -2.1,3.2 -4.4,1.7 -5.4,1 -8.9,4.7 -0.1,7.5 0,0.4 -0.1,1.2 20.3,15.5 18.4,13.9 18.6,13.8 1.3,3 3.4,1.8 2.6,1.1 0.1,4 6.1,-0.6 7.8,-2.8 15.8,-12.5 18.6,-12.2 -2.5,-4 -4.3,-2.9 -2.6,1.2 -2,-3.6 -0.2,-2.7 -3.4,-4.7 2.1,-2.6 -0.5,-4 0.6,-3.5 -0.5,-2.9 0.9,-5.2 -0.4,-3 -1.9,-5.6 -2.6,-11.3 -3.4,-2.6 0,-1.5 -4.5,-3.8 -0.6,-4.8 3.2,-3.6 1.1,-5.3 -1,-6.2 1,-3.3 z"
     style="fill:#f2f2f2;fill-rule:evenodd" />     
     
<path
     inkscape:connector-curvature="0"
     id="Afghanistan"
     onmouseover="displayName(this)"
     data-name="Afghanistan"
     data-id="AF"
     d="m 1369.9,333.8 -5.4,0 -3.8,-0.5 -2.5,2.9 -2.1,0.7 -1.5,1.3 -2.6,-2.1 -1,-5.4 -1.6,-0.3 0,-2 -3.2,-1.5 -1.7,2.3 0.2,2.6 -0.6,0.9 -3.2,-0.1 -0.9,3 -2.1,-1.3 -3.3,2.1 -1.8,-0.8 -4.3,-1.4 -2.9,0 -1.6,-0.2 -2.9,-1.7 -0.3,2.3 -4.1,1.2 0.1,5.2 -2.5,2 -4,0.9 -0.4,3 -3.9,0.8 -5.9,-2.4 -0.5,8 -0.5,4.7 2.5,0.9 -1.6,3.5 2.7,5.1 1.1,4 4.3,1.1 1.1,4 -3.9,5.8 9.6,3.2 5.3,-0.9 3.3,0.8 0.9,-1.4 3.8,0.5 6.6,-2.6 -0.8,-5.4 2.3,-3.6 4,0 0.2,-1.7 4,-0.9 2.1,0.6 1.7,-1.8 -1.1,-3.8 1.5,-3.8 3,-1.6 -3,-4.2 5.1,0.2 0.9,-2.3 -0.8,-2.5 2,-2.7 -1.4,-3.2 -1.9,-2.8 2.4,-2.8 5.3,-1.3 5.8,-0.8 2.4,-1.2 2.8,-0.7 -1.4,-1.9 z"
     style="fill:#f2f2f2;fill-rule:evenodd" />     
     
</svg>

Demo:

enter image description here

Sign up to request clarification or add additional context in comments.

3 Comments

Thanks! Makes sense. Just wasn't aware if Javascript DOM manipulation and this keyword.
@Mathlearner I literally said "SVG elements are scriptable. You can attach event handlers as you'd do with HTML" in my first comment. I obviously didn't make myself clear but I would had gladfully clarified any doubt.
@ÁlvaroGonzález yes sorry. I didn't really know what that meant. Should have asked.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.