2

I apologize if this question has been answered before, I've tried searching but haven't found anything that's helped me with this issue. On to the issue!

I'm attempting to update a community character sheet for Roll20. The sheet uses both HTLM and Javascript. I am trying to set up the sheet so that when a player selects the power type of the spell he's using (think spell school from D&D) it places a different icon onto the button for the spell.

The spells are contained within an HTML button component of a repeating_spell container that is designed to create new, unique buttons for each spell.

I am able to successfully have the image be a static image, the same image no matter the power type, but am unsure of how to dynamically change the HTML img tag based on the power type.

I recently realized that the site I'm designing this character sheet for doesn't allow the id flag within the img tag. I've edited my code to show the change. I'm currently trying to do this using the name= flag, and I'm not sure if the jQuery is finding it as I get no image returned. This is the current code for my img tag:

<img name="powertype" src="#" style="width: 15px; height: 15px; padding: 2px;">

with appropriate JS/jQuery references set in an if/else loop which checks the attribute set by the drop down using the following code:

on("change:spellschool", function(eventinfo){
  getAttrs(["spellschool"], function(v) {
    var spell_school = spellschool.toLowerCase();
    if(spell_school === "biotic") {
      document.getElementById("powertype").src = "https://n7.world/images/spells/biotic.svg";
    }
    else if(spell_school === "combat") {
      document.getElementById("powertype").src = "https://n7.world/images/spells/combat.svg";
    }
    else if(spell_school === "tech") {
      document.getElementById("powertype").src = "https://n7.world/images/spells/tech.svg";
    }
    else {
      return;
    }
  });
});

However when I have the above code set, it simply returns a blank image with padding.

Smeeg699
  • 21
  • 2
  • Possible duplicate of [jQuery Get Selected Option From Dropdown](https://stackoverflow.com/questions/10659097/jquery-get-selected-option-from-dropdown) – Zain Aftab Jul 19 '19 at 04:40

4 Answers4

3

You have some syntactical errors in your JS --

Missing start quote on line 6:
Missing end parentheses on line 10:

Other than that .. I simplified it for you:

$('#spellschool').on('change', function(eventinfo){


    if(this.value == "biotic") {
        document.getElementById("powertype").src =
            "https://n7.world/images/spells/biotic.svg";
    } else if(this.value === "combat") {
        document.getElementById("powertype").src =
            "https://n7.world/images/spells/combat.svg";
    } else if(this.value === "tech") {
        document.getElementById("powertype").src =
            "https://n7.world/images/spells/tech.svg";
    } else {
        return;
    }
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>

<img id="powertype" src="#" style="width: 15px; height: 15px; padding: 2px;">

<select id="spellschool">
<option>Select one</option>
<option value="biotic">biotic</option>
<option value="combat">combat</option>
<option value="tech">tech</option>
</select>
Zak
  • 6,976
  • 2
  • 26
  • 48
  • Hey! Thanks for the quick reply. I tried your code and it still isn't pulling back the image. Doing a little digging, I think it's because Roll20 doesn't allow id elements to be used. I tried changing the img to have name="powertype" and then changing the Javascript to be getElementByName, but that doesn't seem to help... Any other advice? – Smeeg699 Jul 19 '19 at 02:32
  • Try `getElementsByName("powertype")[0]`. Multiple elements are allowed to share the same value in their `name` attribute (unlike with the `id` attribute), so you need to get them all and then specify the first one (using `[0]`). – Cat Jul 19 '19 at 07:26
  • So, I'd had a thought... I think the reason why getElementByName isn't working is because the powertype attribute is in every new spell that gets created and isn't unique enough. I was wondering if a forEach() function might allow me to drop the if/else loop into it so that it checks each repeating_spell by spellid... – Smeeg699 Jul 20 '19 at 06:19
1

I can't link an image in a SO snippet**, but but here's a vanilla-flavored solution that gets the path to the image. Setting the src attribute of an img element equal to pathSpan.innerHTML works to display a .png or .jpg, so unless .svgs behave differently, this should handle your use case.

const dropdown = document.getElementById("dropdown"),
      pathSpan = document.getElementById("imgPath");
dropdown.addEventListener("change", updateImage);

function updateImage(event){
  if(event.target.value == "1"){
    pathSpan.innerHTML = "images/one";
  }
  else if(event.target.value == "2"){
    pathSpan.innerHTML = "images/two";
  }
  else if(event.target.value == "3"){
    pathSpan.innerHTML = "images/three";
  }
  else{
    pathSpan.innerHTML = "";
  }
}
<select id="dropdown">
  <option value="1">1</option>
  <option value="2">2</option>
  <option value="3">3</option>
</select>
<br />
<span>Path to image: </span><span id="imgPath">images/one</span>

**I sit corrected. Linking to a publicly available image on the web works fine, as Zak has proven in their answer. (In production, you may want to link to an image stored locally on your server instead, just so your code can't get broken by changes in an external site.)

Cat
  • 4,141
  • 2
  • 10
  • 18
  • The problem with this is that this isn't something stored on a server I control. This code is for a character sheet used on the roll20.net website. So the code has to constrain to a lot of their requirements. Such as no reserved tags like head or body, and no flags like id because they need things to be unique per sheet since it's likely that there will be multiple copies of this page open as multiple players all use character sheets. – Smeeg699 Jul 20 '19 at 02:13
0

You can also do the same with Vanilla JS.

HTML:

<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>
<body>

    <select id='spellschool'>
        <option value="biotic">Biotic</option>
        <option value="combat">Combat</option>
        <option value="tech">Tech</option>
    </select>

    <img id="powertype" src="#" style="width: 150px; height: 150px; padding: 20px;">
    <script src="main.js"></script>
</body>
</html>

JS:

const select = document.querySelector('#spellschool');
const img = document.querySelector('#powertype');

select.addEventListener('change', () => {
    if (select.value === 'biotic') {
        img.src = 'https://n7.world/images/spells/biotic.svg'
    } else if (select.value === 'combat') {
        img.src = 'https://n7.world/images/spells/combat.svg'
    } else {
        img.src = 'https://n7.world/images/spells/tech.svg'
    }
})

And here's even better example

const select = document.querySelector('#spellschool');
const img = document.querySelector('#powertype');

select.addEventListener('change', (e) => {

    img.src = `https://n7.world/images/spells/${e.target.value}.svg`

    console.log(e.target.value)
})

Where we use the es6 `` (backpacks) to variable selected value to a string.

Elvis S.
  • 362
  • 1
  • 3
  • 13
0

Details are commented in demo. Refer to JavaScript and Forms Tutorial. BTW the attribute id is a fundamental aspect of JavaScript and DOM, and cannot be disregarded by an API so easily. An id must be unique and I gather that you have attempted to assign an id to more than one element hence your unmitigated assumption that id "does not work". If for some reason you cannot get it to work by cutting and pasting into whatever (you did not really describe how you implement the code) -- try changing all ids into name. It's an advantage the <form> element has is the HTMLFormElement API allows seamless association between id and name (they are interchangeable within a form).

// Array of images
const symbols = [
  'https://i.ibb.co/StzKvdm/8.gif',
  'https://i.ibb.co/6Jtv9mK/nec.gif',
  'https://i.ibb.co/6BMt5f9/inv.gif',
  'https://i.ibb.co/FgznhXj/abj.gif',
  'https://i.ibb.co/Jk6X5wk/div.gif',
  'https://i.ibb.co/wwX9xz5/ill.gif',
  'https://i.ibb.co/wKqcxxF/trn.gif',
  'https://i.ibb.co/86gpFf1/cnj.gif',
  'https://i.ibb.co/R6BRtn9/enc.gif'
];

// Reference the first form on page
const form = document.forms[0];
// Collect all inputs, selects, buttons, fieldsets, etc.
const cast = form.elements;
// Reference select
const schools = cast.schools;

// Register select to the change event -- call function changeSymbol()
schools.onchange = changeSymbol;

/** changeSymbol(event)
@Param: event [object]: default Event.Object
As the select changes value so does the src of the element that is in front of it.
//A Pass event object
//B Get event.target (select#schools) value and convert it to a real number
//C Get the element that is in front of select
//D Change that element's src to the url in symbols array at the index of select value
//E* Change the data-idx value to index of select value as well
//F End function
*/
function changeSymbol(event) { //A
  const index = Number(event.target.value); //B
  const images = event.target.nextElementSibling; //C
  images.src = symbols[index]; //D
  images.dataset.idx = index; //E* [OPTIONAL]
  return false; //F
}

// Register form to submit event -- call function magic()
form.onsubmit = magic;

/** magic(event)
@Param: event [object]: default Event.Object
This is left open for whatever purpose when the image is clicked
//A Pass event object
//B Prevent the form from sending data to a server
*/
function magic(event) { //A
  event.preventDefault(); //B
}
:root {
  font: 700 small-caps 3vw/2 'Tempus Sans ITC';
}

body {
  color: #fff;
  background: #000;
}

legend {
  font-size: 2rem
}

select,
input {
  display: inline-block;
  font: inherit;
  line-height: 2rem;
  vertical-align: top;
  margin: 0 10px;
}

select {
  font-size: 1.5rem;
}
<form>
  <fieldset>
    <legend>Schools of Magic</legend>
    <select name='schools'>
      <option value='0' selected>Select a School</option>
      <option value='1'>Necromancy</option>
      <option value='2'>Invocation</option>
      <option value='3'>Abjuration</option>
      <option value='4'>Divination</option>
      <option value='5'>Illusion</option>
      <option value='6'>Transmutation</option>
      <option value='7'>Conjuration</option>
      <option value='8'>Enchantment</option>
    </select>
    <input name='images' src='https://i.ibb.co/StzKvdm/8.gif' type='image' width='220' height='220' data-idx='0'>
  </fieldset>
</form>
zer00ne
  • 41,936
  • 6
  • 41
  • 68
  • The reason I said they won't work is because of Roll20's rules. Quoted the relevant parts below for reference. Quoting from Roll20's Sheet Building page: "Restrictions HTML: In the browser, the character sheet is basically wrapped inside a giant
    tag. Id attributes cannot be used. (Any ID attributes on one character's sheet would affect another character's sheet in the same campaign when opened any DOM functionalities can't be used"
    – Smeeg699 Jul 21 '19 at 18:08
  • Also to address not showing how I implemented the code, I wasn't quite sure of the guidelines in terms of how much code I could/should share. I felt I shared the relevant bits, because the full implementation of the repeating spells section and the JS code bits are a thousand or so lines of code and I felt that was a bit overkill, lol. I appreciate the insight here, will see if I can get it to work. – Smeeg699 Jul 21 '19 at 18:10
  • So did you read the ***whole answer***? *"...try changing all ids into name. It's an advantage the
    element has is the [HTMLFormElement API](https://developer.mozilla.org/en-US/docs/Web/API/HTMLFormElement) allows seamless association between id and name (they are interchangeable within a form)."* ex. in HTML `name=''schools'` instead of `id="schools"`. BTW I looked at some of the code in GH and they use `name`.
    – zer00ne Jul 21 '19 at 18:12
  • Just updated answer use `const form = document.forms[0];` this will reference the first form on page -- id, name, class, etc not needed. Updated the rest just copy and paste inside the form tag. There's no need to change anything now that sufficient info has been provided. – zer00ne Jul 21 '19 at 18:19
  • Also using an array allows you to expand when you can find good images for the sub-schools wild magic, and elementalist (hint use `` tag). I used to play 1st and 2nd ed. – zer00ne Jul 21 '19 at 18:29
  • I appreciate the responses, thank you. Yeah, I read the whole answer, and as you can see having checked the code on GH, everything uses names, as does the sheet I'm working on. Honestly, I'm not sure if, short of looking at the full code, someone will be able to process the constraints I'm working with. I sadly just can't copy/paste what you have above, though I'm going to try editing it to make it fit with what else there is. Wish me luck... Also, not using standard magic schools, this is for a 5e Mass Effect conversion, so using Mass Effect power types (Biotic, Tech, Combat). – Smeeg699 Jul 22 '19 at 08:32
  • This event is not standard: `"change:spellschool"` it must be from the API but all else is standard (albeit crude and inefficient). Change all tags to divs and spans that are not form controls. I read some of the requirements and stopped after reading about divs and spans (it's like 1996 all over again, couldn't stomach anymore) – zer00ne Jul 22 '19 at 09:11