Accessible and dynamic animated information box - javascript

I have started with the example and added to the code to make it:
Dynamic in height
Accessible with JS turned off
Have I done this correct? Will this work on most browsers?
Working version visable here:
Original:
$('#example-links a').hover(function(){
var index = $("#example-links a").index(this);
$('#example-content').animate({"marginTop" : -index*220 + "px"}); // multiply by height+top/bottom padding/margin/border
});
My modified code, it just seems a little long compared to the above:
var maxHeight = 0, container_maxHeight = 0;
var example_content = $("#example-content");
var example_div = example_content.children("div");
example_div.each(function() {
if($(this).height() > maxHeight) {
maxHeight = $(this).height();
container_maxHeight = $(this).outerHeight(true);
}
});
example_div.height(maxHeight);
$("#example-content-container").css({
"overflow":"hidden",
"height": container_maxHeight+"px"
});
$('#example-links a').bind('click mouseover', function(e){
var index = $("#example-links a").removeClass("active").index(this);
$(this).addClass("active");
example_content.stop().animate({"marginTop" : -index*container_maxHeight + "px"});
e.preventDefault();
});
I bind both the click and mouseover because I want it to work via mouseover but then I also want it to work when browsing on a mobile phone that doesn't have a mouse to activate the hover.

Everything seems fine, the only thing I would add to make it more accessible if JS is off, are the section ids. You can check it here.
For each section, you add an id to the wrapping div, and then on your side links you link to that id.
I would clean up your code a little bit more:
(function($){
$(document).ready(function(){
var maxHeight = 0, container_maxHeight = 0,
example_content = $("#example-content"),
example_div = example_content.children("div");
example_div.each(function() {
var $section = $(this);
if($section.height() > maxHeight) {
maxHeight = $section.height();
container_maxHeight = $section.outerHeight(true);
}
});
example_div.height(maxHeight);
$("#example-content-container").height(container_maxHeight);
var $tabs = $('#example-links a');
$tabs.bind('click mouseover', function(e){
var index = $tabs.removeClass("active").index(this);
$(this).addClass("active");
example_content.stop().animate({"marginTop" : -index*container_maxHeight + "px"});
e.preventDefault();
});
});
})(jQuery);

Related

Add width and height attr to all images

I need width and height attributes added to all images on a page via javascript/jquery. This is due to a tool our systems use. I thought a simple each loop, adding height/width attr would suffice. Unfortunately this doesn't seem to work
DEMO https://jsfiddle.net/z86xnqd7/
$('body').find('img').each(function (index) {
var theWidth = index.width();
var theHeight = index.height();
index.attr({
"width": theWidth,
"height": theHeight
});
});
When you inspect element you will notice no width/height attr has been added
jsFiddle : https://jsfiddle.net/CanvasCode/z86xnqd7/6/
You need to do your each on the load event, you need to make sure your image has loaded before you check its height and width. Also you want to use $(this) instead of index.
$(function () {
$('img').load(function () {
var theWidth = $(this).width();
var theHeight = $(this).height();
$(this).attr({
"width": theWidth,
"height": theHeight
});
});
});
It's because index is real iteration index (e.g. 1, 2...). Use $(this) or add second parameter to function header function (index, element) {}:
$.each($('img'), function () {
$(this).attr({
width: $(this).width(),
height: $(this).height()
});
});
Problem here is that you try to get the width and height from the index which is just a number and no jQuery object. Try this - it will do the job:
$('body').find('img').each(function(i, elem) {
var $this = $(this);
var theWidth = $this.width();
var theHeight = $this.height();
$this.attr({"width": theWidth, "height": theHeight});
});
See js fiddle: https://jsfiddle.net/g4nn2pk0/2/

javascript scroll to bottom of div class

Hi I am trying to implement a simple chatbox in django and was wondering how to scroll to the bottom of a div class using javascript? Basically when the page loads I would like so that users can see the most recent message sent to them instead of the least recent.
I had to do this recently for a similar thing. I found a basic jquery plug-in that will smoothly scroll an element onto the screen.
(function($) {
$.fn.scrollMinimal = function() {
var cTop = this.offset().top;
var cHeight = this.outerHeight(true);
var windowTop = $(window).scrollTop();
var visibleHeight = $(window).height();
if (cTop < windowTop) {
$('body').animate({'scrollTop': cTop}, 'slow', 'swing');
} else if (cTop + cHeight > windowTop + visibleHeight) {
$(jQuery.browser.webkit ? "body": "html")
.animate({'scrollTop': cTop - visibleHeight + cHeight}, 'slow', 'swing');
}
};
}(jQuery));
which is used like this:
$('#chat').scrollMinimal();
Well, the basic script is set the scrollTop equal to scrollHeight, so you need a script like this:
var DIV = document.getElementById('theDIVElement');
DIV.scrollTop = DIV.scrollHeight;
You only need to change theDIVElement to your DIV id.
This is the script I used in my chat:
<SCRIPT LANGUAGE="JavaScript">
<!--
function myScroll() {
window.scrollBy(0,01)
setTimeout('myScroll()',100); }
if (document.layers || document.all)
myScroll()
//--></SCRIPT>
This is also nice for when new messages are added, if you scroll to the bottom too fast, it's hard on your eyes while you're trying to read.

How to reset to original values?

It looks like it keeps adding a new newHeight and a newDistance each time i click, I am trying to save original height with a global var at the top and using data to do that but i get weird results, basically i should be able to reset newDistance and newHeight to first original values as per before to run the lot with a click but it doesn't and i get new added values each time i click breaking my layout as a result:
talents = $(".talenti");
filter = $(".filtra");
genHeight = $("#container").data($("#container").height());
filter.click(function(e) {
e.preventDefault();
if (talents.hasClass("opened")) {
$(".nasco").slideToggle();
$("#wrapNav").slideToggle("10", "linear");
talents.removeClass('opened');
filter.addClass('opened');
$("#container").css("height", genHeight);
} else {
filter.addClass('opened');
};
if (filter.hasClass("opened")) {
$("#wrapNav").slideToggle("10", "linear", function(){
$("#sliding-navigation").slideToggle();
var newHeight = $("#container").height() + $("#wrapNav").outerHeight(true);
var newDistance = newHeight - $("#container").height() + 22;
$("#container").animate({height: newHeight}, 50,function(){
$(".box").animate({top: newDistance});
});
});
}
});
talents.click(function(e) {
e.preventDefault();
if (filter.hasClass("opened")) {
$("#sliding-navigation").slideToggle();
$("#wrapNav").slideToggle("10", "linear");
filter.removeClass('opened');
talents.addClass('opened');
$("#container").css("height", genHeight);
} else {
talens.addClass('opened');
};
if (talents.hasClass("opened")) {
$("#wrapNav").slideToggle("10", "linear", function(){
$(".nasco").slideToggle();
var newHeight = $("#container").height() + $("#wrapNav").outerHeight(true);
var newDistance = newHeight - $("#container").height() + 156;
$("#container").animate({height: newHeight}, 50,function(){
$(".box").animate({top: newDistance});
});
});
}
});
Anyone?
So, based on the code I could download about 20min ago from your test site, I managed to get it working with the following code:
$(document).ready(function(){
// placeholder to contain the original height...
var original_height = 0;
talents = $(".talenti");
filter = $(".filtra");
filter.click(function(e){
e.preventDefault();
if (filter.hasClass('opened')){
filter.removeClass('opened');
// toggle the wrapping, just with a zero top coordinate...
$("#wrapNav").slideToggle("10", "linear", function(){
$("#sliding-navigation").hide();
$(".box").animate({top: '0px'});
});
// reset to the original height...
$("#container").height(original_height);
}
else {
// get the original height if it's not already set...
if (original_height == 0)
original_height = $("#container").height();
filter.addClass('opened');
if (talents.hasClass("opened"))
{
$(".nasco").hide();
$("#wrapNav").slideToggle();
talents.removeClass('opened');
}
// toggle the wrapping with a height of the nav as top coordinate...
$("#wrapNav").slideToggle("10", "linear", function(){
$("#sliding-navigation").slideToggle(true, function(){
// need the height of the nav before we know how far to move the boxes...
var newHeight = $("#wrapNav").outerHeight(true);
$(".box").animate({top: newHeight});
// set the container's new height, much like you had...
$("#container").height(original_height + newHeight);
});
});
}
});
talents.click(function(e) {
e.preventDefault();
if (talents.hasClass('opened')) {
talents.removeClass('opened');
// toggle the wrapping, just with a zero top coordinate...
$("#wrapNav").slideToggle("10", "linear", function(){
$(".nasco").hide();
$(".box").animate({top: '0px'});
});
// reset to the original height...
$("#container").height(original_height);
}
else {
// get the original height if it's not already set...
if (original_height == 0)
original_height = $("#container").height();
talents.addClass('opened');
if (filter.hasClass("opened"))
{
$("#sliding-navigation").hide();
$("#wrapNav").slideToggle();
filter.removeClass('opened');
}
// toggle the wrapping with a height of the nav as top coordinate...
$("#wrapNav").slideToggle("10", "linear", function(){
// need the height of the nav before we know how far to move the boxes...
$(".nasco").slideToggle(true, function(){
var newHeight = $("#wrapNav").outerHeight(true);
$(".box").animate({top: newHeight});
// set the container's new height, much like you had...
$("#container").height(original_height + newHeight);
});
});
}
});
});
A few points adding food for thought:
I simplified the multiple if statements to make it easier to understand and process
I used hide() to avoid messy animation problems if you clicked on FILTER multiple times in a row
I only adjusted the top coordinates of the boxes to achieve this
I would have preferred to contain the boxes in a more general container, allowing for easier animation and management, but I understand that wordpress doesn't always give you the most room to work, so this should get you on your way!
It might not be completely what you're looking for in your animation, but it's a working example of the code you had and should get you 90% of the way...hope this helps! :)
What about using the data collection of the container element rather than a global variable i.e. at the top record the height
$("#container").data('height', $("#container").height());
then to use
$("#container").data('height');
i.e. to reset the height
$("#container").css({height: $("#container").data('height') });
I feel a bit suspicious about how the global variable is working. Worth a try maybe

jQuery scroll to div on hover and return to first element

I basically have a div with set dimensions and overflow: hidden. That div contains 7 child divs (but only shows one at a time) that I would like to be smoothly scrolled through vertically when their respective links are hovered.
However, the first section (div) doesn't have a link and is the default section when no link is hovered.
Take a look at this jsFiddle to see a basic structure of what I'm talking about: http://jsfiddle.net/YWnzc/
I've attempted to accomplish this with jQuery scrollTo but haven't been able to get it to work.
Any help would be greatly appreciated. Thanks.
Something like this?
http://jsfiddle.net/YWnzc/5/
code:
jQuery("#nav").delegate("a", "mouseenter mouseleave", function (e) {
var i, self = this,
pos;
if (e.type == "mouseleave") {
i = 0;
}
else {
//Find out the index of the a that was hovered
jQuery("#nav a").each(function (index) {
if (self === this) {
i = index + 1; //the scrollTop is just calculated from this by a multiplier, so increment
return false;
}
});
}
//Find out if the index is a valid number, could be left undefined
if (i >= 0) {
//stop the previous animation, otherwise it will be queued
jQuery("#wrapper").stop().animate({
scrollTop: i * 200
}, 500);
//I would retrieve .offsetTop, but it was reporting false values :/
}
e.preventDefault();
});
FYI : That JSFIDDLE you sent me to went to MooTools framework, not jQuery... fyi. (might be why its not working?
Copy and paste this code exactly and it will work in jQuery for animated scrolling.
Try this for smooth scrolling within the DIV, I tested it - it works great. You
$(function() {
function filterPath(string) {
return string
.replace(/^\//,'')
.replace(/(index|default).[a-zA-Z]{3,4}$/,'')
.replace(/\/$/,'');
}
var locationPath = filterPath(location.pathname);
var scrollElem = scrollableElement('#wrapper');
// Any links with hash tags in them (can't do ^= because of fully qualified URL potential)
$('a[href*=#]').each(function() {
// Ensure it's a same-page link
var thisPath = filterPath(this.pathname) || locationPath;
if ( locationPath == thisPath
&& (location.hostname == this.hostname || !this.hostname)
&& this.hash.replace(/#/,'') ) {
// Ensure target exists
var $target = $(this.hash), target = this.hash;
if (target) {
// Find location of target
var targetOffset = $target.offset().top;
$(this).click(function(event) {
// Prevent jump-down
event.preventDefault();
// Animate to target
$(scrollElem).animate({scrollTop: targetOffset}, 400, function() {
// Set hash in URL after animation successful
location.hash = target;
});
});
}
}
});
// Use the first element that is "scrollable" (cross-browser fix?)
function scrollableElement(els) {
for (var i = 0, argLength = arguments.length; i <argLength; i++) {
var el = arguments[i],
$scrollElement = $(el);
if ($scrollElement.scrollTop()> 0) {
return el;
} else {
$scrollElement.scrollTop(1);
var isScrollable = $scrollElement.scrollTop()> 0;
$scrollElement.scrollTop(0);
if (isScrollable) {
return el;
}
}
}
return [];
}
});
FYI : Credit for this code does not go to me as an individual developer, although I did slightly tweak the code. The owner and creator of this code is Chris Coyier and you can find more about this scrolling code here:
http://css-tricks.com/snippets/jquery/smooth-scrolling/
Here's a working example: http://jsfiddle.net/YWnzc/7/
And the code (pretty similar to rizzle's, with a couple changes that I'll explain):
$('a').hover(function(){
var selector = $(this).data('section');
var scrollAmount = $(selector).offset().top + $('#wrapper')[0].scrollTop - 129;
$('#wrapper').animate({scrollTop: scrollAmount}, 250);
},function(){
$('#wrapper').animate({scrollTop: 0}, 250);
});
First, var selector = $(this).data('section'); because in jsFiddle, the href attribute was returning the full path of the page + the hash. So I changed it to an html5 data attribute (data-section).
The next line is similar to rizzle's, except that we grab the offset of the section and add it to the current scrollTop value of the #wrapper. As he pointed out, there are some weird offset issues going on still, and I found that subtracting 129 did the trick. While this 129 number might seem like something that is likely to break, I did test out changing the sizes of the sections, making them not equal, etc, and it continued to work. I'm using Chrome, and perhaps a non-webkit browser would need a different constant to subtract. But it does seem like that 129 number is at least some kind of constant.
The rest should be pretty self-explanatory.
One thing to note: as you move your cursor over the <a> tags, the content of the #wrapper div will seem to jump around, but that's just because the mouseleave part of the hover event briefly gets triggered as the cursor moves. I'm sure you can solve that one though :)
$("#nav a").hover(function () {
var sectionName = $(this).attr("href");
var sectionPos = $(sectionName).offset().top;
var wrapperPos = $("#wrapper").offset().top;
var wrapperScroll = $("#wrapper").scrollTop();
var scrollPos = sectionPos - wrapperPos + wrapperScroll;
$("#wrapper").stop().animate({scrollTop:scrollPos}, 600);
}, function () { $("#wrapper").stop().animate({scrollTop:0}, 600); });

How do I drag multiple elements at once with JavaScript or jQuery?

I want to be able to drag a group of elements with jQuery, like if I selected and dragged multiple icons on the Windows desktop.
I found the demo of threedubmedia's jQuery.event.drag:
http://threedubmedia.com/code/event/drag/demo/multi
http://threedubmedia.com/code/event/drag#demos
I think this plugin is great. Is this good and popular library? Do you know websites or applications which use it?
Are there any other libraries or plugins to drag multiple objects?
Can jQuery UI drag multiple objects?
var selectedObjs;
var draggableOptions = {
start: function(event, ui) {
//get all selected...
selectedObjs = $('div.selected').filter('[id!='+$(this).attr('id')+']');
},
drag: function(event, ui) {
var currentLoc = $(this).position();
var orig = ui.originalPosition;
var offsetLeft = currentLoc.left-orig.left;
var offsetTop = currentLoc.top-orig.top;
moveSelected(offsetLeft, offsetTop);
}
};
$(document).ready(function() {
$('#dragOne, #dragTwo').draggable(draggableOptions);
});
function moveSelected(ol, ot){
console.log(selectedObjs.length);
selectedObjs.each(function(){
$this =$(this);
var pos = $this.position();
var l = $this.context.clientLeft;
var t = $this.context.clientTop;
$this.css('left', l+ol);
$this.css('top', t+ot);
})
}
I am the author of the of the threedubmedia plugins. I added this functionality for supporting multiple elements, because I could not find a satisfactory solution anywhere else.
If you need a solution that works with the jQuery UI, here is a plugin which adds some multi-drag functionality, though the demos don't seem to work correctly in Firefox for Mac.
http://www.myphpetc.com/2009/11/jquery-ui-multiple-draggable-plugin.html
This worked for me.
Fiddle here:
var selectedObjs;
var draggableOptions = {
start: function(event, ui) {
//get all selected...
if (ui.helper.hasClass('selected')) selectedObjs = $('div.selected');
else {
selectedObjs = $(ui.helper);
$('div.selected').removeClass('selected')
}
},
drag: function(event, ui) {
var currentLoc = $(this).position();
var prevLoc = $(this).data('prevLoc');
if (!prevLoc) {
prevLoc = ui.originalPosition;
}
var offsetLeft = currentLoc.left-prevLoc.left;
var offsetTop = currentLoc.top-prevLoc.top;
moveSelected(offsetLeft, offsetTop);
selectedObjs.each(function () {
$(this).removeData('prevLoc');
});
$(this).data('prevLoc', currentLoc);
}
};
$('.drag').draggable(draggableOptions).click(function() {$(this).toggleClass('selected')});
function moveSelected(ol, ot){
console.log("moving to: " + ol + ":" + ot);
selectedObjs.each(function(){
$this =$(this);
var p = $this.position();
var l = p.left;
var t = p.top;
console.log({id: $this.attr('id'), l: l, t: t});
$this.css('left', l+ol);
$this.css('top', t+ot);
})
}
Thanks to ChrisThompson and green for the almost-perfect solution.
I wanted to add (this coming up high in google), since none of the plugins in this thread worked and it is not nativity supported by jquery ui, a simple elegant solution.
Wrap the draggable elements in a container and use an event to drag them all at once, this allows for singles draggables and multidraggables (but not really selective draggables).
jQuery(document).click(function(e) {
if(e.shiftKey) {
jQuery('#parent-container').draggable();
}
});
Check this out:
https://github.com/someshwara/MultiDraggable
Usage:$(".className").multiDraggable({ group: $(".className")});
Drags the group of elements together. Group can also be an array specifying individual elements.
Like:$("#drag1").multiDraggable({ group: [$("#drag1"),$("#drag2") ]});
Put your items into some container and make this container draggable. You will need to set handle option to be a class of your item element. Also you will need to recalculate items position after drag. And obviously when you deselect items you have to take them from this container and put back to their origin.
This is what i used, Worked in my case.
function selectable(){
$('#selectable').selectable({
stop: function() {
$('.ui-selectee', this).each(function(){
if ($('.ui-selectee').parent().is( 'div' ) ) {
$('.ui-selectee li').unwrap('<div />');
}
});
$('.ui-selected').wrapAll('<div class=\"draggable\" />');
$('.draggable').draggable({ revert : true });
}
});
};
there is Draggable in the jquery UI
all you would have to do is:
$(selector).draggable(); // and you are done!
see example here: http://jsfiddle.net/maniator/zVZFq/
If you really want multidragging you can try using some click events to hold the blocks in place
$('.drag').draggable();
$('.drag').click(function(){
console.log(this, 'clicked')
var data = $(this).data('clicked');
var all = $('.all');
if(data == undefined || data == false){
$(this).data('clicked', true);
this.style.background = 'red';
$(this).draggable('disable');
if(all.children().length <= 0){
all.draggable().css({
top: '0px',
left: '0px',
width: $(window).width(),
height: $(window).height(),
'z-index': 1
});
}
var top = parseInt(all.css('top').replace('px','')) +
parseInt($(this).css('top').replace('px',''))
var left = parseInt(all.css('left').replace('px','')) +
parseInt($(this).css('left').replace('px',''))
$(this).css({
top: top,
left: left
})
$('.all').append($(this));
}
else {
$(this).data('clicked', false);
this.style.background = 'grey';
$(this).draggable('enable');
$('body').append($(this));
if(all.children() <= 0){
all.draggable('destroy');
}
/*
var top = parseInt(all.css('top').replace('px','')) -
parseInt($(this).css('top').replace('px',''))
var left = parseInt(all.css('left').replace('px','')) -
parseInt($(this).css('left').replace('px',''))
$(this).css({
top: top,
left: left
})*/
}
})
See example here: http://jsfiddle.net/maniator/zVZFq/5

Categories

Resources