I need some help adjusting some Javascript code to work in particular scenarios. I've never dabbled in JS so I don't really know how to go about doing this, but I understand the concept. I'm using this tutorial to help me.

The example he provides has a set of images and a textual link for each one. Clicking on a link adjusts the padding to view that current image in the designated frame. However, in the site I'm working on I only plan to have two links - arrows - for next and previous. What would be the best way to adapt the JavaScript to handle this?

Original Code:

Code:
$(document).ready(function() {
   $("#slide1-1").click(function() {
      $("#slide1_images").css("left","0");
   });
   $("#slide1-2").click(function() {
      $("#slide1_images").css("left","-450px");
   });
   $("#slide1-3").click(function() {
      $("#slide1_images").css("left","-900px");
   });
   $("#slide1-4").click(function() {
      $("#slide1_images").css("left","-1350px");
   });
});


I'm thinking I'd need something like this:

Code:
$(document).ready(function() {
   $("#next").click(function() {
      $("#slide1_images").css("left",""+450);
   });
   $("#previous").click(function() {
      $("#slide1_images").css("left",""-450px);
   });
});


Like I said, I'm no JavaScript coder so the above *is* incorrect syntax and execution. But, basically I'd need to add 450 pixels to the left element for each "next" and 450 for each "previous" link click. I'm also thinking of adding the "text links" he has but as the photos themselves. While his photos are in a frame, all/some of mine will be visible to the user, which they may instinctively click. If they do, I want that photo to get center attention - or at least progress the string of photos in that direction.


Secondly. This isn't as particular. My navigation has a :hover psuedo-class that adds a background. I need that to slide from nav link to nav link as the :hover changes. Is this CSS3 doable?
Not tested, but I imagine something like this would do the trick. Sort of in the vein of "Beware of bugs in the above code; I have only proved it correct, not tried it."


Code:
// Define the starting image, number of images, and image width
var currentImage = 0,
      imageCount = 4,
      imageWidth = 450;

// Specify how we want to update the image offset
var updateImage = function(){$("#slide1_images").css("left", -1*currentImage*imageWidth+'px');};

// Add click events
$(document).ready(function() {
        $("#next").click(function() {
                 // Check if currentImage+1 is within bounds. If not, cycle.
                 currentImage = (currentImage+1<imageCount)?currentImage+1: 0;
                 updateImage();
        });

        $("#previous").click(function() {
                 // Check if currentImage-1 is within bounds. If not, cycle.
                 currentImage = (currentImage>0)?currentImage-1: imageCount-1;
                 updateImage();
        });
});
Eh, you really don't need anything that complicated.


Code:
$(document).ready(function() {
    var $slide = $("#slide1_images");

    $("#next").click(function(){
        var left = $slide.css("left");
        $slide.css("left",curLeft + 450);
    });

    $("#prev").click(function(){
        var curLeft = $slide.css("left");
        $slide.css("left",curLeft - 450);
    });
});


Basically exactly what you were thinking, Alex. The only difference is that you need to store the current value of `left` into a variable and then add/subtract 450 from that.

If you wanted to get a little more complex, without having to reconfigure the code every time you add or remove an image, you could do something else...

Now, this really depends on your HTML structure, but I assume, for simplicities sake that it is something like so:

Code:
<div id="slide">
    <div id="slide1_images">
        <div id="slide1-1"></div>
        <div id="slide2-1"></div>
        <div id="slide3-1"></div>
        <div id="slide4-1"></div>
    </div>
</div>

<a href="#" id="next"></a>
<a href="#" id="prev"></a>


The javascript code would look like so:

Code:
$(document).ready(function(){
    $("#next").on("click",function(){
        moveSlide("left");
    });

    $("#prev").on("click",function(){
        moveSlide("right");
    });
});

function moveSlide(direction) {
    var $slide = $("#slide1_images"),
        $children = $slide.children(),
        increment = 450,
        maxLeft = $children.length() * increment,
        newLeft = $slide.css("left");

    if(direction=="left") {
        newLeft += increment;

        if (newLeft > maxLeft) newLeft = maxLeft;
    } else if(direction=="right") {
        newLeft -= increment;

        if (newLeft < 0) newLeft = 0;
    }

    $slide.css("left",newLeft);
}


Also, I put `#slide_images` into a variable, but its not really necessary. The reason I did is for optimization purposes. If you're afraid you may get confused, don't continue reading, haha. Basically, `$` is a variable with the type of `function` that contains the jQuery library. It's simply the same variable as `jQuery`. So, every time you use `$('#mySelector')` you're calling a function that has to look up that element in the DOM again. Granted for the most part it is just a wrapper for document.querySelectorAll, which is incredibly fast native code, but that's only in modern browsers and it is still a wrapper which makes it the slightest bit slower than the real thing... When you store the result of the jQuery function into a variable, you don't have to call the function every time. Instead, the result is already there. This really isn't a big deal at *all* for smaller sites, but when it comes to enterprise applications, it can make a difference in performance.

Also, for readability I like to prepend my variable names with the dollar sign to symbolize that these variables contain a jQuery object. It's to help differentiate between other variables. Just an idea to help you read your code better, if it helps for you too Razz


And, like rthprog, I didn't test the code I wrote. Works in theory Very Happy
swivelgames wrote:

Code:
$(document).ready(function() {
    var $slide = $("#slide1_images");

    $("#next").click(function(){
        var left = $slide.css("left");
        $slide.css("left",curLeft + 450);
    });

    $("#next").click(function(){
        var curLeft = $slide.css("left");
        $slide.css("left",curLeft - 450);
    });
});

Ideally things shouldn't break if you hit next a few too many times. Good call on avoiding DOM lookups over and over again Smile

swivelgames wrote:

Also, for readability I like to prepend my variable names with the dollar sign to symbolize that these variables contain a jQuery object. It's to help differentiate between other variables.

I can tell you like PHP Smile
rthprog wrote:
Ideally things shouldn't break if you hit next a few too many times. Good call on avoiding DOM lookups over and over again Smile
Indeed! That's why I supplied the second solution. Similar to (and inspired by) yours, it just takes out the necessity to configure it.


rthprog wrote:
I can tell you like PHP Smile
Haha, a lot of people say that, but it's actually because the shorthand for jQuery is `$`, so it associates jQuery shorthand with the variable. Nothing to do with PHP Smile
I do have four images in my mock, but I'd like the source to be easily extensible to incorporate any number of images. Below, I've posted the source pertaining to the scrolling images.


Code:
      <div id="image-scroll">
         <div id="arrows">
            <a href="#" id="left"><span class="arleft"></span></a>
            <a href="#" id="right"><span class="arright"></span></a>
         </div>
         
         <div id="imagelist">
            <img class="imageinscroll" src="res/img/placeholder.png">
            <img class="imageinscroll" src="res/img/placeholder.png">
            <img class="imageinscroll" src="res/img/placeholder.png">
            <img class="imageinscroll" src="res/img/placeholder.png">
         </div>
      </div>


Then below, I've seperated the JavaScript into what's included in the page header and what's in an included javascript file.


Code:
      <script type="text/javascript" src="res/scripts/jQuery.js"></script>
      
      <script type="text/javascript">
         $(document).ready(function(){
             $("#left").on("click",function(){
                 moveSlide("left");
             });
         
             $("#right").on("click",function(){
                 moveSlide("right");
             });
         });
      </script>
   
      <script type="text/javascript" src="res/scripts/function.js?1"></script>
   
   </head>



Code:
function moveSlide(direction) {
    var $slide = $("#imagelist"),
        $children = $slide.children(),
        increment = 360,
        maxLeft = $children.length() * increment,
        newLeft = $slide.css("left");

    if(direction=="left") {
        newLeft += increment;

        if (newLeft > maxLeft) newLeft = maxLeft;
    } else if(direction=="right") {
        newLeft -= increment;

        if (newLeft < 0) newLeft = 0;
    }

    $slide.css("left",newLeft);
}


I went with this JS because it looked much cleaner and was easier to understand and it didn't rely on a predetermined number of images. However, it's still not scrolling. I get an error in Chrome the crops up between lines 5 & 6 on the functions.js file that reads "Uncaught TypeError: Property 'length' of object [object Object] is not a function."

I'd post a source page but I don't think I am able to. However, I wouldn't mind sharing it privately with the two of you if you guys care to take some time to look it over. I understand Joseph is adjusting his source a bit though, so I'll try the tweaked code when he posts it.
Here's what I came up with for my implementation, slightly tailored towards yours with an explanation.


Code:
<div id="image-scroll">
   <div id="arrows">
      <a href="#" id="left"><span class="arleft"></span></a>
      <a href="#" id="right"><span class="arright"></span></a>
   </div>
         
   <div id="imagelist">
      <img class="imageinscroll" src="res/img/placeholder.png">
      <img class="imageinscroll" src="res/img/placeholder.png">
      <img class="imageinscroll" src="res/img/placeholder.png">
      <img class="imageinscroll" src="res/img/placeholder.png">
   </div>
</div>



Code:
$(document).ready(function(){
   $('#left').on('click',function(){
      moveSlide("left");
   });

   $('#right').on('click',function(){
      moveSlide("right");
   });
});

function moveSlide(direction) {
   // Define our list of elements
   var $imageList = $('#imagelist'),
       $items = $imageList.find('.imageinscroll'),

       // Define the scrolling increment
       increment = 450,

       // Determine how far we want to scroll
       maxLeft = -1 * (increment * ($items.length - 1)),

       // Retrieve the current left for calculation
       curLeft = $imageList.data('newLeft') || $imageList.css("left"),

       // Remove all characters that aren't used in numbers
       newLeft = parseInt(curLeft.replace(/[^0-9\-\.]+/g,''));


   // Add or Subtract based on direction
   if(direction=="right") {
      newLeft -= increment;
   } else if(direction=="left") {
      newLeft += increment;
   }

   // Did we go to far??
   if( newLeft <= maxLeft ){
      newLeft = maxLeft;
   }

   // Are we back to the start?
   if(newLeft >= 0) {
      newLeft = 0;
   }

   // Store the new left for easy access later
   // This is because of animation. You don't need this if you're not going to animate the movement
   $imageList.data('newLeft',newLeft.toString());

   // Animate the new position
   $imageList.stop().animate({ "left": newLeft },250);
}



It is significantly longer only because of the comments. I hope the comments help. Let me know if they don't. If you remove the comments and the extra linebreaks in there it should be about the same size. Questions? Smile
I'll give this a shot, I'll also leave the comments in for now as I'm learning this to pass it on to a friend - and I'll include the URL to this topic as well.

Questions:
For the "$items = $this.find('.imageinscroll'), " it counts the number of .imageinscroll elements on the page, correct?

When the JavaScript inspector throws back an error that reads "$this is not defined," do I just need to tell JS it's a variable?" The error appears on line 4, "$items = $this.find('.imageinscroll'), "
Bah! That was my fault. The `$this` was supposed to be `$imageList`. Pulled from my code which was a bit different than the one I supplied. My apologies. Smile

To answer your question, I'll explain a little about how jQuery works, as well as give you an incredibly priceless resource for it.

Here's the code in question
Code:
var $imageList = $('#imagelist'),
    $items = $imageList.find('.imageinscroll');
The variable `$imageList` now contains the jQuery Object referring to the `#imagelist` element. But because this is a jQuery Object, we can execute jQuery methods on it. The `find` method searches through all the elements inside of the `#imagelist` element for the specified selector.

So, just like jQuery("#imagelist") looks for every element inside of the document that has an ID of "imagelist", the same goes for this function but with a narrower scope. The only difference is that it looks for every element inside of the "imagelist" element (as opposed to the whole document) whose class contains the "imageinscroll" class. It just narrows the results.

The jQuery Object, mind you, is a type of array. When jQuery returns a jQuery Object its really a list of DOM elements that match the criteria. This allows you to treat it like an array too. So, the `Array` variable type has a prototype property called `length` which (obviously) contains how many elements it is. So, when you do `find` it narrows the result. Then you can count how many there are by using `length`, like so:

Code:
jQuery("#imagelist").find(".imageinscroll").length
The above would return '4' because there's four DOM elements with the "imageinscroll" class inside of "imagelist".


Another function called `children` does something similar, except it only search for elements who are immediate children. For instance...

HTML

Code:
<div id="myDiv">
    <div class="abc"></div>
    <div class="xyz">
        <div class="abc"></div>
    </div>
    <div class="abc"></div>
</div>
JavaScript
Code:
jQuery('#myDiv').children('.abc')
The code above would only return the first and last DIVs with the class of "abc" because they were immediate children. The second div.abc is actually inside of xyz, making it a grandchild element. If we wanted to find every single div.abc under div#myDiv we could just use `find` like so:
Code:
jQuery('#myDiv').find('.abc')
The above would look for all decedents, not just immediate ones.

The resource: http://api.jquery.com/{jQueryMethodName}

jQuery does a good job of organizing and documenting their library.

Checkout http://api.jquery.com/find and http://api.jquery.com/children
The corrected code does not return an error but the list of images don't seem to move. I fear it could be the HTML and will be working on getting the images to display properly before I dismiss the error to JS.

Though, for future reference, where would it be appropriate to append !important to the end of '$imageList.stop().animate({ "left": newLeft },250);'?


UPDATE
It wasn't my HTML - but I'm still having issues with that, which I'll likely ask about if I can't resolve it soon - but the Javascript. It appears I couldn't animate inside another div, despite that container div, #image-scroll, not having a fixed width. I've adjusted the JS accordingly. However, it only scrolls to the left and 315 pixels and it won't continue in either direction. Any words of wisdom?


Code:
function moveSlide(direction) {
   // Define our list of elements
   var $imageList = $('#image-scroll');
       $items = $imageList.find('.imagelist'),

       // Define the scrolling increment
       increment = 315,

       // Determine how far we want to scroll
       maxLeft = -1 * (increment * ($items.length - 1)),
       maxRight = 1 * (increment * ($items.length - 1)),

       // Retrieve the current left for calculation
       curLeft = $imageList.data('newLeft') || $imageList.css("left"),

       // Remove all characters that aren't used in numbers
       newLeft = parseInt(curLeft.replace(/[^0-9\-\.]+/g,''));


   // Add or Subtract based on direction
   if(direction=="right") {
      newLeft -= increment;
   } else if(direction=="left") {
      newLeft += increment;
   }

   // Did we go to far??
   if( newLeft <= maxLeft ){
      newLeft = maxLeft;
   }

   // Are we back to the start?
   if(newLeft >= maxRight) {
      newLeft = maxRight;
   }

   // Store the new left for easy access later
   // This is because of animation. You don't need this if you're not going to animate the movement
   $imageList.data('newLeft',newLeft.toString());

   // Animate the new position
   $imageList.stop().animate({ "left": newLeft },250);
}


Adding in maxRight helped get it past that left: 30.

UPDATE 2
I managed to fix the scroll, I changed the following:

Code:
 $items = $imageList.find('.imagelist'),

//to

 $items = $imageList.find('.imageinscroll'), 


After that, Clicking left went right, and clicking right the thing scrolled left. After swapping the increment math, it works as expected. A few tweaks here and there and I should be golden! Fixed the HTML as well!

UPDATE 3
Played around with the CSS & JavaScript a bit more once I got it working and it turns out jQuery can only modify CSS properties that are already present for each element. So, the change of 'left' wasn't working earlier because that property didn't exist for the ID/Class I was using. I'll be tweaking source and HTML to better fit what I've walked away with here.

Now I just need to make sure the scroll stops at a certain threshold, which shouldn't be too difficult. However, getting the background to change position based on which navigation is hovered over will be tricky. No idea where to start but I think I got JS down to formulate a solution once I get pointed in the right direction.

Basically. Hover over "About" and a background appears/slides in, change the hover to "Contact" and the background slides behind it, forming to it's width as well.

UPDATE 4
I've copied the part of the site I was referring to in Update 3 and used basic colors for ease of communication.

When the user hovers over a nav link, the background for that selection becomes blue. Is it possible to have the blue slide and resize as the user hovers over different navs? I'm thinking it could come from and leave to behind the name with the red background. I don't need it to resize down, just horizontally, though the sub-menu will slide down.
Just as a general comment for debugging HTML, CSS, and Javascript, I hope you're using something like Firebug. It can help a great deal in streamlining debugging of these sorts of issues. Also, in Firefox, Ctrl-Shift-J (on Windows, anyway) brings up the Error Console, which will show you many of your fatal Javascript errors and typos, thouhg of course not more subtle logic errors. If you already knew about both of those, then feel free to ignore me. Smile
My IDE has a debugger and both Chrome and Safari do the same with the "Inspect Element" then selecting "Resources" and the related JavaScript file. It may not be as easy/streamlined as FireBug though.

My only issue now is that I have a code block for what I want to do with the navigation but it's too different to dissect and apply in this situation with my knowledge. I hope to find the resource again and copy the demo navigation onto a blank page and figure out what does what that way. I'll link it here once I find it as well.
comicIDIOT wrote:
Is it possible to have the blue slide and resize as the user hovers over different navs?
My gut reaction would be an onMouseOver for the element in question that would run Javascript to change the blue thing's position and size, but I suspect your question is more complex than that.
That's exactly what I want it to do. I'll google onMouseOver Navigation Menus and see what comes up.
comicIDIOT wrote:
That's exactly what I want it to do. I'll google onMouseOver Navigation Menus and see what comes up.
Navigation menus? Don't you just want to move a blue shape when something else gets onMouseOver'd? What is the blue thing, exactly? Maybe that's what I'm missing.
http://comicidiot.com/help/

The blue background is part of the navigation. I was thinking it could hide behind the logo and slide out when an element is hovered.

I just realized the source I found is a paid download, so my idea of dissecting the source may not be entirely sound, so here's a link to the page that demos what I have in mind. But instead of the glowing top & bottom bars, it's the blue background on my page. The websites own navigation has a similar concept.
Never animate with left/right/top/bottom - or anything padding or margin related. Anything that changes the DOM == DO NOT ANIMATE!

Instead, use a transform: translate3d(). Then it will be accelerated and smooth, especially on mobile.
Kllrnohj wrote:
Never animate with left/right/top/bottom - or anything padding or margin related. Anything that changes the DOM == DO NOT ANIMATE!

Instead, use a transform: translate3d(). Then it will be accelerated and smooth, especially on mobile.


Is there any particular reason why? Glitchy? Is it an old method that will be obsolete with advanced CSS3 properties? What would be the best way to translate the current script into one that works with transform; I assume you're referring to the transform CSS property. I'll look into it more, in the meantime it works but I'll do my best to animate the menu with transform since I still need to figure out how to do that.
comicIDIOT wrote:
Is there any particular reason why? Glitchy? Is it an old method that will be obsolete with advanced CSS3 properties? What would be the best way to translate the current script into one that works with transform; I assume you're referring to the transform CSS property. I'll look into it more, in the meantime it works but I'll do my best to animate the menu with transform since I still need to figure out how to do that.


Modifying the DOM forces the page to be repainted (and for a layout to happen). This is slow, especially on mobile. By slow I mean such a paint can easily take ~100ms, depending on how much of the page is animating. When you use a translate3d, the content is put onto its own layer which is then typically GPU composited (doesn't have to be, but is on all modern mobile browsers). This means when moving stuff around, it's basically free. There's no painting that happens, instead a GL texture is simply moved. It's super duper free if you do your animation using a CSS keyframe animation instead of JavaScript - as on Android in particular the animation will then run in a different thread, meaning the page (and your javascript) is free to be as slow as it wants without causing the animation to hiccup or stutter. I don't know if iOS also does off-thread keyframe animations, but it could. So use those instead.
Not sure if you are still looking for anything on this subject but, I think this contains some code you were looking for. I saw your post last night and made a simple program someone may find useful. This would work well for a small gallery. Just make 1 picture 500*2000 would make 4 pics to view onscreen or you can make them smaller bigger or whatever. You can even add more images. Adjust it however you feel necessary. I tried to limit use of code for simplicity and size. You can even expand on this code using the hidden attribute I added and create a drop down or pop up menu if you'd like using a table nav or maybe more div elements in your tags.

Code:

<!doctype html><html lang="en"><head><meta charset="UTF-8"><title>Gallery by basicman :)</title>
</head><body><script text/javascript>
window.addEventListener('load', canvasMenu, false);   
function canvasMenu(){
   var windowX=0;
       var theCanvas = document.getElementById('gallerycanvas');
       var context = theCanvas.getContext('2d');
   var galleryImage=new Image();
   galleryImage.addEventListener('load', galleryStart , false);
   galleryImage.src="galleryImage.jpg";
   var windowWidth=500;
   var windowHeight=500;
   var button1 = document.getElementById('panel-gallery-toggle');
   var button2 = document.getElementById('panel-gallery2-toggle');
   function galleryStart() {
      startUp();}
   function galleryImages(){
      //events      
      context.drawImage(galleryImage, windowX, 0,windowWidth,windowHeight,0,0,windowWidth,windowHeight);}
      button1.onclick = function(){      
windowX-=500;
context.drawImage(galleryImage, windowX, 0,windowWidth,windowHeight,0,0,windowWidth,windowHeight);}
      button2.onclick = function(){
         windowX += 500;
         context.drawImage(galleryImage, windowX, 0,windowWidth,windowHeight,0,0,windowWidth,windowHeight);}
   function startUp(){
      galleryImages();}}
</script>
   <style type="text/css" media="screen">   
         /*button for left direction*/
#ui #panel-container-gallery #panel-gallery-toggle {
transition:width 2s;
-moz-transition:width 2s; /* Firefox 4 */
-webkit-transition:width 2s; /* Safari and Chrome */
-o-transition:width 2s; /* Opera */
   /* button on bottom of page */
   bottom: 0%;
 left: 0%;
   color: black;
   font-size: 2em;
   width: 108px;
   height: 35px;
   text-align: center;
   font-weight: bold;
   -webkit-border-radius: 5px;
   -moz-border-radius: 5px;
   -o-border-radius: 5px;
   -ms-border-radius: 5px;
   border-radius: 5px;
   background-color: rgba(12, 168, 5,1);
   text-shadow: #F2F2F2 4px 4px 5px;
}
#ui #panel-container-gallery:hover #panel-gallery-toggle:hover {background-color: rgba(12, 16, 150,.6);width:300px;
transition-timing-function: linear;
/* Firefox 4: */
-moz-transition-timing-function: linear;
/* Safari and Chrome: */
-webkit-transition-timing-function: linear;
/* Opera: */
-o-transition-timing-function: linear;}
/*button for right direction*/
#ui #panel-container-gallery2 #panel-gallery2-toggle {
transition:width 2s;
-moz-transition:width 2s; /* Firefox 4 */
-webkit-transition:width 2s; /* Safari and Chrome */
-o-transition:width 2s; /* Opera */
   position: absolute;
   /*button on bottom of page */
   bottom: 0%;
    right: 0%;
   color: black;
   font-size: 2em;
   width: 108px;
   height: 35px;
   text-align: center;
   font-weight: bold;
   -webkit-border-radius: 5px;
   -moz-border-radius: 5px;
   -o-border-radius: 5px;
   -ms-border-radius: 5px;
   border-radius: 5px;
   background-color: rgba(12, 168, 5,1);
   text-shadow: #F2F2F2 4px 4px 5px;}
#ui #panel-container-gallery2:hover #panel-gallery2-toggle:hover {background-color: rgba(12, 16, 150,.6);width:300px;
transition-timing-function: linear;
/* Firefox */
-moz-transition-timing-function: linear;
/* Safari and Chrome */
-webkit-transition-timing-function: linear;
/* Opera */
-o-transition-timing-function: linear;}
      </style>
<div style="position: absolute; top: 50px; left: 50px;">
<canvas id="gallerycanvas" width="500" height="500">
 Your browser does not support the HTML 5 Canvas. 
</canvas>
<div id="ui">
    <div id="panel-container-gallery" class="hidden">
               <button onclick="button1" id="panel-gallery-toggle">Left</button>
               <div id="panel-gallery">               
               </div>
               </div>
               <div id="panel-container-gallery2" class="hidden">   
               <button onclick="button2" id="panel-gallery2-toggle">Right</button>
               <div id="panel-gallery2">               
               </div>
               </div>
               </div>
</div>
</body>
</html>
  
Register to Join the Conversation
Have your own thoughts to add to this or any other topic? Want to ask a question, offer a suggestion, share your own programs and projects, upload a file to the file archives, get help with calculator and computer programming, or simply chat with like-minded coders and tech and calculator enthusiasts via the site-wide AJAX SAX widget? Registration for a free Cemetech account only takes a minute.

» Go to Registration page
Page 1 of 1
» All times are UTC - 5 Hours
 
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum

 

Advertisement