Is it possible to base an <<if>> on whether an image link is valid?

Hello!

In SugarCube (2.30.0) could you, for example, have something like:

<<if $image is a valid image link>>
[img[$image]]
<</if>>

Or would you have to venture into other kinds of coding?

Thanks!

1 Like

I found a way to check whether an image successfully loaded, but that doesn’t really answer your question.

I’m curious why you would want to do this though? I can only come up with a few reasons why you would, and they all seem prone to disaster. :sweat_smile:

Well, you definitely can’t do it like that.

However, if you use something like the image caching system in my Universal Inventory System (UInv), then you might be able to get something like that to work. (See a live example here.)

In UInv you’d use the cacheImages() function to tell the browser to start downloading the images (you’d need to do this in a passage somewhere before the passage where you need to check if the image is valid to give it time to load). Then you could use the getCachedImageObject() function to determine the status of the image. So you might do something like this:

<<run UInv.cacheImages($imagePath, $imageName)>>

to start loading the image at that path. Then, a couple of passages later, you could do:

<<set _imgObj = UInv.getCachedImageObject($imagePath, $imageName)>>
<<if _imgObj.status === "Loaded">>
	[img[_imgObj.src]]
<<else _imgObj.status !== "Error">>
	/* The browser is still trying to download the image.  */
	/* It's up to you how you want to handle this case.  */
<</if>>

That would show the image if it had loaded successfully.

I’m not sure how you want to handle the case where the browser is still trying, so you’ll have to replace the commented out section with your own code for how you want to handle that case.

You don’t need all of UInv for that, so feel free to rip out the image cache code if you want.

Note that the cacheImages() function also allows you to set up an event handler which triggers a widget or function when the image either loads or fails to load, if that’s something you’d find useful.

Hope that helps. :slight_smile:

1 Like

Heh heh, it must sound odd.

Basically what I did is set up a textbox to modify a variable, then:

<<set $image to "image path" + $variable + ".png">>
[img[$image]]

It works perfectly: I have a word as the filename for an image, and if you type in that word and press enter, the image associated with that word appears.

The problem is if you type something else, a misspelling or some random letters or nothing, I don’t want the broken image link icon to show up. I could do <<if $variable is "word">> for all the words you can type in that have a corresponding image, but there are over two hundred. If I could set it to just not display if $image isn’t a valid image link, it would save a lot of space at least, you know?

Or I could figure out some other way to get the broken image link to not show up.

Hmm. I just figured out a kind of hacky way to do it. I just tested and it works in both Chrome and IE 11.

<img style="display:none" onload="this.style.display='block'" src="test.jpg">

What this does is it automatically sets the image to not being displayed. If it loads, it’ll run “onload” after, which sets to it to visible. If the link is bad, it never loads, and so it stays invisible and you don’t get a broken image icon.

Edit: I actually forgot HiEv posted a solution earlier. :sweat_smile: His is probably a safer solution than mine, if you want to (or are already) using his library. Mine is a total hack, but it does seem to work with minimal code, at least.

Found the answer (or a comment that led to the answer) on a stackoverflow topic: I just need to wrap [img[$image]] in a span with style=“text-indent: -100%;”

Unfortunately I’ve already done a whole bunch of work setting up the hundreds of “if $variable is “word” or $variable is “word”…” so that was a lot of time I didn’t need to use up… but at least I save a vast amount of space not using it. :sweat_smile: That’s replaced a 12½’’ by 6’’ wall of text with about two words.

Oh, I see. I think that works if you give it an alt text. When it’s broken, it’s supposed to show the text instead, but with the text-indent set to -100%, it puts it off the screen to the left. That’s an even hackier solution than my hack job. :sweat_smile: But hey, if it’s working for you, go with it.

Hm, now I’m actually trying to get it to not offset the image as well… (Apparently [img[$image]] counts as text even when it displays as an image.)

I’ve also tried a long list of techniques for replacing an image if it fails to load, and none of them worked.

FYI - If you do need to do something like that in the future, you can just make an array of all of the possible words in your StoryInit like this:

<<set setup.wordlist = ["aardvark", "bear", "cat"]>>

and then, instead of using what you describe above, you can check to see if a word is in that list by just doing:

<<if setup.wordlist.includes($variable)>>

You do that on the “setup” object because the array doesn’t ever change and so that it doesn’t take up space in the game’s history. (See the .includes() method for details.)

Basically, any time you find your code is repeating itself, there’s generally a shortcut you should be using instead.

I didn’t realize all you wanted was the image hidden if it failed to load. I thought you wanted your code to do something specific if the image was a valid link.

If you want the image to load a blank image (really a 1x1 transparent GIF) on failure you can do this:

<img @src="$filename" onerror="$(this).attr('src', 'data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEAAAAALAAAAAABAAEAAAI=')">

or if you want it to show a particular image file when loading the image fails:

<img @src="$filename" onerror="$(this).attr('src', 'errorImage.png')">

and that will load the “errorImage.png” file if loading the file named in $filename fails.

Hope that helps! :slight_smile:

I see: what I thought wouldn’t work was getting $image to display in <img>, because src="" didn’t work. Thanks again HiEv! :slightly_smiling_face: