I want to make something beautiful, but I need some help with this paintbrush here.
March 29, 2012 5:26 PM   Subscribe

I want to make an awesome web resource, but have hit a wall on the technical side of it and need to know what to ask for. I want to make an image with a slider that reveals and hides different triggerable pop-ups depending on where you move the slider.

I asked this question over a year ago, didn't get the answer I wanted, but am still obsessing about it and am slowly becoming a crazy person.

There are two requirements: it can't be flash and it can't be google (or hosted or dependent on any external service or company).

The image itself will be a map, but when I talk to people about it they keep getting tripped up on this like I want to be able to zoom in and manipulate the map. I don't. I want a single static image that has an interactive slider that shows and reveals different triggerable icons/buttons/whatever, that reveal boxes of text/images/embedded videos/links/etc.

So far, I've gotten suggestions for things like simile and the like, but nothing I've seen looks remotely close to it. A few people I've talked to have suggested an amalgamation of jquery, mysql, and javascript or php, but nothing concrete as to what specifically to look for or, subsequently, how exactly it would be put together.

Currently, I have a jquery slider floating in space unattached to anything. Everything suggests that something like this could exist, but no one has made it yet. I'm bright enough that I can put some of it together but not smart enough to make it out of whole cloth (I can handle css and html, not writing javascript, php, or processing, though I could probably tinker).

Lastly, it's going to have a lot of data points and it'll be a living resource that will be updated over time.

HELP!
posted by history is a weapon to Computers & Internet (25 answers total) 6 users marked this as a favorite
 
So, for clarity, is what you want something like a slideshow that rather than being controlled by "previous" and "next" buttons has a slider control, and rather than showing just an image for each slide shows and reveals rectangles/divs containing buttons, text, images, videos, et cetera?

In any case, this sounds a bit more complicated than learning to use a paintbrush. You are probably going to have to learn some javascript to do this.

(Honestly, it sounds like you're obliquely describing something along the lines of an "interactive timeline", but that's just a guess.)
posted by XMLicious at 5:50 PM on March 29, 2012


Sorry, that should be "reveals and hides rectangles/divs containing buttons, text, images, videos, et cetera?"
posted by XMLicious at 5:52 PM on March 29, 2012


Also, it might help if you linked to the previous question.
posted by XMLicious at 5:53 PM on March 29, 2012 [1 favorite]


Definitely need more details. Could you create and scan a drawing or two?
posted by dawkins_7 at 6:00 PM on March 29, 2012


Response by poster: The original question is here: http://ask.metafilter.com/172759/The-map-is-not-the-territory

It would be map, no need to zoom or pan around, just a still image, with icons on it, that when clicked, would produce boxes of info (text/images/links etc). This much I can make (though only with all the data hosted on that page, not called from a database). But I want it to have a timeline at the bottom that you can drag it around and the different clickable items appear and disappear depending on what year you are at.
posted by history is a weapon at 6:29 PM on March 29, 2012


This isn't very simple for someone new to coding javascript, and even an experienced programmer would probably expect to do a fair number of testing and debugging cycles, but if you're really dedicated to this I'd recommend breaking it up into two phases:

First, instead of the slider, make a row of buttons equivalent to the notches on the slider, so that pressing each button brings up a different "slide". Work on it to make sure that you can press the buttons in any order and it doesn't leave the wrong icons on the map or anything of that sort.

When you have that all worked out, now you're ready to hook it up to a slider control - the jQuery one or something like it. You will have to learn to work with what is called "event handling" in javascript: when someone grabs the slider control and moves it around it will generate "events" and you'll have to write code that interprets what the events mean and properly clear out the old icons and display the correct ones based on where the slider has been moved to. You'll have to figure out whether you just want to change what's displayed when the user lets go of the slider control (simplest) or if you want them to see things changing in "real time" as they move the slider back and forth (more complicated).

And, don't forget to test in different browsers. (For simplicity's sake you probably want to make it work in only the newest versions of the browsers, especially Internet Explorer.)
posted by XMLicious at 6:55 PM on March 29, 2012 [1 favorite]


Also, in case it isn't clear, this all needs to be something that's in a single HTML page with CSS and javascript: the buttons in that first phase I describe can't do a form submit or otherwise reload the page to make their changes if it's all eventually going to be hooked up to a javascript slider control. Each button will be calling one or more javascript functions when pressed.
posted by XMLicious at 7:13 PM on March 29, 2012


(Also, consequently, this isn't something you'd be using PHP or MySQL or anything server-side for. A really sophisticated version of it might, but that would increase the complexity of the project by an order of magnitude.)
posted by XMLicious at 7:15 PM on March 29, 2012


I haven't done much of this sort of thing, but I think you need to look at jquery slider and in the slide callback, clear a div and then add the elements, buttons, etc to it with jquery .html() call.
posted by rainy at 7:48 PM on March 29, 2012 [1 favorite]


I had another thought... if you could find a slideshow application that provides a slider control and also lets you associate description text with each image, maybe it could be rigged so that you could stuff the HTML code for your map icons in with the description text.
posted by XMLicious at 9:56 PM on March 29, 2012


I just want to add that you should also read a primer on javascript and maybe buy a book. If you haven't done any coding before, this probably still won't take all that much time, maybe a few weekends will be enough, if all goes well. Use chrome browser and its development tools console for debugging. You can also pay somebody to do this on sites like elance.com, or you can go as far as you can on your own and then pay a javascript coder to help you debug and finish it up.
posted by rainy at 11:41 PM on March 29, 2012


I think I see what you have in mind. As I told another person on Ask wanting to make a dynamic page recently, you can do this.

Work through the jQuery Getting Started tutorial (and perhaps the AJAX and CSS tutorials at W3Schools as well) and you'll have a much better idea about how to do what you're looking for.

To grossly oversimplify
<!DOCTYPE html>
<html>
<head>
  <link href="http://ajax.googleapis.com/ajax/libs/jqueryui/1.8/themes/base/jquery-ui.css" rel="stylesheet" type="text/css"/>
  <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.5/jquery.min.js"></script>
  <script src="http://ajax.googleapis.com/ajax/libs/jqueryui/1.8/jquery-ui.min.js"></script>
  <style type="text/css">
    #slider { margin: 10px; }
    
    div.infobox  { visibility: hidden; }
    div.selected { visibility: visible;}
  </style>
  <script>
    $(document).ready(function() {
      $("#slider").slider();

      $( "#slider" ).bind( "slide", function(event, ui) {
          if (ui.value % 25 == 0) {
              $(".selected").removeClass("selected");   
              $("#"+ui.value).addClass("selected");    
          }
      });

      $("#0").addClass("selected");
    });
  </script>
</head>
<body>

<div class="infobox" id="0"  >First</div>
<div class="infobox" id="25" >Second</div>
<div class="infobox" id="50" >Third</div>
<div class="infobox" id="75" >Fourth</div>
<div class="infobox" id="100">Fifth</div>
  
<div id="slider"></div>

</body>
</html>
To accomplish what you're trying to do, you'll need to be able to write the code to retrieve data into and position as many div elements as you want to have "pins" on your map. Start small, do some cut-paste-and-tinkering, and you'll get there.
posted by ob1quixote at 11:42 PM on March 29, 2012 [2 favorites]


ob1quixote example is very good, one thing I'd add is that as a first approach (and maybe sufficient for you), you can store data in a javascript array in the page itself so that you don't need to retrieve it via ajax but just look it up from the array. Look at jquery docs on how to clear divs and add stuff into divs. jquery is really great and simple and their docs are some of the best.
posted by rainy at 11:50 PM on March 29, 2012


rainy: "store data in a javascript array in the page itself so that you don't need to retrieve it via ajax"

I thought about saying just that, rainy, but since history is a weapon mentioned database calls in a followup I wound up leaving that part out.

In any case, rainy is right. Unless the data is already in a database or there's a helluva lot of it (say more than few megabytes worth of text), there's no reason not to stuff it right into the page and call it a day. If you're trying to anticipate a situation where you get a lot of traffic at some point, without some non-trivial engineering on the back-end a database based solution isn't going to be more likely to be scalable than just jamming it all in one file.
posted by ob1quixote at 12:07 AM on March 30, 2012


ob1quixote: I noticed that OP talked about DB but it sounded like he might just have thought that's where data is supposed to be stored. One other concern about ajax calls to DB in this case is smoothly updating the map as the slider is moved. I also thought about loading the page and then continuing to preload more data in the background sequentially, and loading specific page if user slides to it but it's not preloaded yet, but that's rather complicated and probably an overkill for this.
posted by rainy at 12:16 AM on March 30, 2012 [1 favorite]


rainy: "One other concern about ajax calls to DB in this case is smoothly updating the map as the slider is moved."

Actually, now that you put it that way, I agree. As long as the load time stays reasonable it's almost certainly better to have the page load with a div containing an animated "loading…please wait" graphic visible and the main content div hidden, delay load all the text (and maybe all of the graphics too), then hide the loading div and show the content div. Done right, this would have the added benefit of at least partially separating the content from the presentation which would make maintenance easier.
posted by ob1quixote at 12:51 AM on March 30, 2012


ob1quixote's example is a start, but may not behave correctly if the slider is moved too quickly. The "if" statement ensures that updates only occur when the slider is on a value divisible by 25 (a value you should feel free to customize if you use this approach) but there's no guarantee that an event will get fired as the user drags the slider past such a point. If the slider is at 49 one moment* and 51 the next, the slider's event handler will not do anything, even though you really would want it to change the display.

A possible suggestion: replace
      $( "#slider" ).bind( "slide", function(event, ui) {
          if (ui.value % 25 == 0) {
              $(".selected").removeClass("selected");   
              $("#"+ui.value).addClass("selected");    
          }
      });
with
var lastInterval = -1;
var intervalSeparator = 25; /* HEY CHANGE THIS FOR YOUR USE CASE */
$( "#slider" ).bind( "slide", function(event, ui) {
    var currentInterval = Math.floor(ui.value / intervalSeparator);
    if (lastInterval != currentInterval) {
        $(".selected").removeClass("selected");   
        $("#"+ui.value).addClass("selected");    
        lastInterval = currentInterval;
    }
})

This code conceptually divides the slider's possible values into intervals. Within an interval, what is displayed is not changed; between intervals, what is displayed will change. The intervals in the code above are 25 units wide (on the assumption that you set up your divs with ids separated by that number as in ob1quixote's example, but you are encouraged to change that number (intervalSeparator) for your specific needs). From one moment* to the next, it checks to see what interval the user has positioned the slider's pointer in (currentInterval). It then says "if the interval the cursor is in NOW is different from what it was when I last checked, then I need to update what gets displayed".

The concept of intervals may not fit particularly well with your needs, e.g. if some items are to be displayed for a span of the slider's values that only partially overlaps with the display of other items. Also, it may not be appropriate for the intervals to be equally wide, e.g. if some items are to be displayed for longer than other items. Let us know if either of these conditions holds.

*By "moment" I mean "time when the OS updates the mouse position information available to the browser". The OS will tell the browser every few milliseconds what the position of the mouse is, but the mouse can move more than one pixel (and slider value) in that time. The OS will not say "oh and by the way it passed through these intermediate values" and the browser is certainly not required to figure out the intermediate values and tell its javascript engine.
posted by a snickering nuthatch at 5:49 AM on March 30, 2012


Jpfed: "ob1quixote's example is a start, but may not behave correctly if the slider is moved too quickly."

It occurred to me overnight that to do this for real you'll want to fiddle with the options on the slider so that it "naturally" notches at your preferred interval rather than do modulus division on a 1-100 range. You could set the min, max, and step properties to, for example, min = 1861, max = 1865, and step = 1 to set up an American Civil War History slider.

In my example above that would translate into changing the "slider" definition and "slide" callback function to
      $("#slider").slider({
        min: 1861,
        max: 1865,
        step: 1
      });

      $( "#slider" ).bind( "slide", function(event, ui) {
              $(".selected").removeClass("selected");   
              $("#"+ui.value).addClass("selected");    
      });
You also have to change the id attributes on the five "infobox" divs and the initial selected id to match the slider. You get the idea though.
posted by ob1quixote at 11:17 AM on March 30, 2012


Response by poster: I'm still figuring this out and may want to ask some more questions, but at the very least, I may wish to purchase some alcoholic beverages for a few people here.
posted by history is a weapon at 5:49 AM on March 31, 2012


Response by poster: Okay. I'm going to try and keep my cool in asking this next question, but I am incredibly excited by these answers here. What do I do if an event is longer than a single year. Some things will be a single year (e.g., 2012), but other things will be multiple years or decades. Is there a way it can be changed so that the id="1500" can be something like id="1500-1545" and will show up whenever the timeline scrub is anywhere in this range (e.g., stopped at 1532)?

Again, thank you so much.
posted by history is a weapon at 6:09 AM on March 31, 2012


Just a general javascript if clause: if (1500 <>
You'll probably want to read a javascript primer if you work on this project.
posted by rainy at 9:12 AM on March 31, 2012


Sorry, should be: if (1500 <> {
loop through divs and select all that match range
}

Read up on jquery selectors on how to do this loop. Maybe
somebody who uses jquery often can post here.
posted by rainy at 9:17 AM on March 31, 2012


Doh, metafilter shows the code in preview but then loses it.. last try:

if (1500 <= ui.value <= 1545) {...}
posted by rainy at 9:18 AM on March 31, 2012


A couple ways to do what you want (these also allows you to have elements that are displayed for partially-overlapping spans of the range of slider values):

First approach (lower performance, but might be ok if you don't have too much to display) is to change the ids on those divs from having just one number to being a space-separated list of numbers. Then:

instead of looking for the element whose id exactly matches the slider value and displaying it (which is what $("#" + ui.value).addClass('selected') does in ob1quixote's example)

you can look for all elements whose id contains the slider value and display those with something like $('div[id*="' + ui.value + '"]').addClass('selected');

----------------------------------------------------------

Second approach (do this if something is displayed for many slider values and you'd rather not type all of them out in a space-separated list):
/* don't put spaces in your id values */
var popups = [
  {id:"civilWar", start:1860, end:1864}
  {id:"gettysburgAddress", start: 1863, end: 1863},
  {id:"appomattox", start:1864, end:1864}
];

/* then, in the slider event handler, after you removeClass("selected") do this: */
for (var index=0;index<popups.length;index++) {
  var popup = popups[index];
  if (ui.value > popup.start && ui.value < popup.end) {
    $("#" + popup.id).addClass("selected");
  }
}
Then, you can make the ids on the divs you're going to be displaying and hiding into descriptive names like "civilWar" and "gettysburgAddress". Just remember to not have spaces in those names; a common way of dealing with this is to mash the words together but capitalizeWordsAfterTheFirstOne (called camelCase).
posted by a snickering nuthatch at 10:21 AM on March 31, 2012


I think Jpfed's second approach is the best choice. Just make sure you .addClass("selected") to all the IDs you want to be displayed when the page loads.
posted by ob1quixote at 2:51 PM on March 31, 2012


« Older Breaking Bad News   |   What do I need to do to keep my career moving?... Newer »
This thread is closed to new comments.