Django Horizontal Scroll on Top of Changelist - javascript

I have the same problem as depicted here by mozman2:
"In my Django changelist there are lots of columns that means there is a scrollbar at the bottom of the list. Is it possible to get a scrollbar to appear at the top so I don't need to scroll down"
The solution from the given link seemed to help mozman2. However, I cannot reproduce it. Hence I tried copy-pasting the code from
https://github.com/avianey/jqDoubleScroll#readme
In particular, I copied this file from the repository to MyApp/static/admin/js/
jquery.doubleScroll.js
The file looks like this:
/*
* #name DoubleScroll
* #desc displays scroll bar on top and on the bottom of the div
* #requires jQuery
*
* #author Pawel Suwala - http://suwala.eu/
* #author Antoine Vianey - http://www.astek.fr/
* #version 0.5 (11-11-2015)
*
* Dual licensed under the MIT and GPL licenses:
* http://www.opensource.org/licenses/mit-license.php
* http://www.gnu.org/licenses/gpl.html
*
* Usage:
* https://github.com/avianey/jqDoubleScroll
*/
(function( $ ) {
jQuery.fn.doubleScroll = function(userOptions) {
// Default options
var options = {
contentElement: undefined, // Widest element, if not specified first child element will be used
scrollCss: {
'overflow-x': 'auto',
'overflow-y': 'hidden',
'height': '20px'
},
contentCss: {
'overflow-x': 'auto',
'overflow-y': 'hidden'
},
onlyIfScroll: true, // top scrollbar is not shown if the bottom one is not present
resetOnWindowResize: false, // recompute the top ScrollBar requirements when the window is resized
timeToWaitForResize: 30 // wait for the last update event (usefull when browser fire resize event constantly during ressing)
};
$.extend(true, options, userOptions);
// do not modify
// internal stuff
$.extend(options, {
topScrollBarMarkup: '<div class="doubleScroll-scroll-wrapper"><div class="doubleScroll-scroll"></div></div>',
topScrollBarWrapperSelector: '.doubleScroll-scroll-wrapper',
topScrollBarInnerSelector: '.doubleScroll-scroll'
});
var _showScrollBar = function($self, options) {
if (options.onlyIfScroll && $self.get(0).scrollWidth <= $self.width()) {
// content doesn't scroll
// remove any existing occurrence...
$self.prev(options.topScrollBarWrapperSelector).remove();
return;
}
// add div that will act as an upper scroll only if not already added to the DOM
var $topScrollBar = $self.prev(options.topScrollBarWrapperSelector);
if ($topScrollBar.length == 0) {
// creating the scrollbar
// added before in the DOM
$topScrollBar = $(options.topScrollBarMarkup);
$self.before($topScrollBar);
// apply the css
$topScrollBar.css(options.scrollCss);
$(options.topScrollBarInnerSelector).css("height", "20px");
$self.css(options.contentCss);
var scrolling = false;
// bind upper scroll to bottom scroll
$topScrollBar.bind('scroll.doubleScroll', function() {
if (scrolling) {
scrolling = false;
return;
}
scrolling = true;
$self.scrollLeft($topScrollBar.scrollLeft());
});
// bind bottom scroll to upper scroll
var selfScrollHandler = function() {
if (scrolling) {
scrolling = false;
return;
}
scrolling = true;
$topScrollBar.scrollLeft($self.scrollLeft());
};
$self.bind('scroll.doubleScroll', selfScrollHandler);
}
// find the content element (should be the widest one)
var $contentElement;
if (options.contentElement !== undefined && $self.find(options.contentElement).length !== 0) {
$contentElement = $self.find(options.contentElement);
} else {
$contentElement = $self.find('>:first-child');
}
// set the width of the wrappers
$(options.topScrollBarInnerSelector, $topScrollBar).width($contentElement.outerWidth());
$topScrollBar.width($self.width());
$topScrollBar.scrollLeft($self.scrollLeft());
}
return this.each(function() {
var $self = $(this);
_showScrollBar($self, options);
// bind the resize handler
// do it once
if (options.resetOnWindowResize) {
var id;
var handler = function(e) {
_showScrollBar($self, options);
};
$(window).bind('resize.doubleScroll', function() {
// adding/removing/replacing the scrollbar might resize the window
// so the resizing flag will avoid the infinite loop here...
clearTimeout(id);
id = setTimeout(handler, options.timeToWaitForResize);
});
}
});
}
}( jQuery ));
I then told django about the file using
class Media:
js = (
'//ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js', # jquery
'js/jquery.doubleScroll.js', # project static folder
)
Afterwards I followed with a collectstatic-command:
...
Copying '/MyApp/static/admin/js/jquery.doubleScroll.js'
...
1 static file copied to '/MyApp/static', 123 unmodified.
However, the horizontal scroll-bar on top doesn't show.
In the github repository it is suggested to use the double-scrollbar by
$(document).ready(function() {
$('.double-scroll').doubleScroll();
});
Where do I put this? I tried using it on the same .js-File instead of the starting
(function( $ ) {
...
};
This didn't help neither.
I guess I am missing out on something?

I solved my problem by knowing the syntax for django - jquery.
Here's how:
I got 2 new .js - Files, one which just calls the function of the double-scroll provided (calling.js) and the js-Files of the authors (doubleScroll.js) itself.
In your adminModel in admin.py you put in:
class Media:
js = ('//ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js',
'admin/js/doubleScroll.js',
'admin/js/calling.js',)
The '//ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js' - link is necessary to make native jquery possible to run, if I understood correctly.
calling.js:
django.jQuery(document).ready(function() {
setTimeout(() => $('.results').doubleScroll({resetOnWindowResize: true}), 245);
});
doubleScroll.js:
/*
* #name DoubleScroll
* #desc displays scroll bar on top and on the bottom of the div
* #requires jQuery
*
* #author Pawel Suwala - http://suwala.eu/
* #author Antoine Vianey - http://www.astek.fr/
* #version 0.5 (11-11-2015)
*
* Dual licensed under the MIT and GPL licenses:
* http://www.opensource.org/licenses/mit-license.php
* http://www.gnu.org/licenses/gpl.html
*
* Usage:
* https://github.com/avianey/jqDoubleScroll
*/
(function( $ ) {
jQuery.fn.doubleScroll = function(userOptions) {
// Default options
var options = {
contentElement: undefined, // Widest element, if not specified first child element will be used
scrollCss: {
'overflow-x': 'auto',
'overflow-y': 'hidden',
'height': '20px'
},
contentCss: {
'overflow-x': 'auto',
'overflow-y': 'hidden'
},
onlyIfScroll: true, // top scrollbar is not shown if the bottom one is not present
resetOnWindowResize: false, // recompute the top ScrollBar requirements when the window is resized
timeToWaitForResize: 30 // wait for the last update event (usefull when browser fire resize event constantly during ressing)
};
$.extend(true, options, userOptions);
// do not modify
// internal stuff
$.extend(options, {
topScrollBarMarkup: '<div class="doubleScroll-scroll-wrapper"><div class="doubleScroll-scroll"></div></div>',
topScrollBarWrapperSelector: '.doubleScroll-scroll-wrapper',
topScrollBarInnerSelector: '.doubleScroll-scroll'
});
var _showScrollBar = function($self, options) {
if (options.onlyIfScroll && $self.get(0).scrollWidth <= $self.width()) {
// content doesn't scroll
// remove any existing occurrence...
$self.prev(options.topScrollBarWrapperSelector).remove();
return;
}
// add div that will act as an upper scroll only if not already added to the DOM
var $topScrollBar = $self.prev(options.topScrollBarWrapperSelector);
if ($topScrollBar.length == 0) {
// creating the scrollbar
// added before in the DOM
$topScrollBar = $(options.topScrollBarMarkup);
$self.before($topScrollBar);
// apply the css
$topScrollBar.css(options.scrollCss);
$(options.topScrollBarInnerSelector).css("height", "20px");
$self.css(options.contentCss);
var scrolling = false;
// bind upper scroll to bottom scroll
$topScrollBar.bind('scroll.doubleScroll', function() {
if (scrolling) {
scrolling = false;
return;
}
scrolling = true;
$self.scrollLeft($topScrollBar.scrollLeft());
});
// bind bottom scroll to upper scroll
var selfScrollHandler = function() {
if (scrolling) {
scrolling = false;
return;
}
scrolling = true;
$topScrollBar.scrollLeft($self.scrollLeft());
};
$self.bind('scroll.doubleScroll', selfScrollHandler);
}
// find the content element (should be the widest one)
var $contentElement;
if (options.contentElement !== undefined && $self.find(options.contentElement).length !== 0) {
$contentElement = $self.find(options.contentElement);
} else {
$contentElement = $self.find('>:first-child');
}
// set the width of the wrappers
$(options.topScrollBarInnerSelector, $topScrollBar).width($contentElement.outerWidth());
$topScrollBar.width($self.width());
$topScrollBar.scrollLeft($self.scrollLeft());
}
return this.each(function() {
var $self = $(this);
_showScrollBar($self, options);
// bind the resize handler
// do it once
if (options.resetOnWindowResize) {
var id;
var handler = function(e) {
_showScrollBar($self, options);
};
$(window).bind('resize.doubleScroll', function() {
// adding/removing/replacing the scrollbar might resize the window
// so the resizing flag will avoid the infinite loop here...
clearTimeout(id);
id = setTimeout(handler, options.timeToWaitForResize);
});
}
});
}
}( jQuery ));
Dont forget to run
collectstatic

jQuery is included in Django, so only one .js file is required.
1. admin.py
# Created by BaiJiFeiLong#gmail.com at 2022/5/5
from django.contrib import admin
from django.contrib.auth.models import User
class UserAdmin(admin.ModelAdmin):
model = User
class Media(object):
js = (
"js/doubleScroll.js",
)
2. js/doubleScroll.js
/*
* #name DoubleScroll
* #desc displays scroll bar on top and on the bottom of the div
* #requires jQuery
*
* #author Pawel Suwala - http://suwala.eu/
* #author Antoine Vianey - http://www.astek.fr/
* #version 0.5 (11-11-2015)
*
* Dual licensed under the MIT and GPL licenses:
* http://www.opensource.org/licenses/mit-license.php
* http://www.gnu.org/licenses/gpl.html
*
* Usage:
* https://github.com/avianey/jqDoubleScroll
*/
(function( $ ) {
jQuery.fn.doubleScroll = function(userOptions) {
// Default options
var options = {
contentElement: undefined, // Widest element, if not specified first child element will be used
scrollCss: {
'overflow-x': 'auto',
'overflow-y': 'hidden',
'height': '20px'
},
contentCss: {
'overflow-x': 'auto',
'overflow-y': 'hidden'
},
onlyIfScroll: true, // top scrollbar is not shown if the bottom one is not present
resetOnWindowResize: false, // recompute the top ScrollBar requirements when the window is resized
timeToWaitForResize: 30 // wait for the last update event (usefull when browser fire resize event constantly during ressing)
};
$.extend(true, options, userOptions);
// do not modify
// internal stuff
$.extend(options, {
topScrollBarMarkup: '<div class="doubleScroll-scroll-wrapper"><div class="doubleScroll-scroll"></div></div>',
topScrollBarWrapperSelector: '.doubleScroll-scroll-wrapper',
topScrollBarInnerSelector: '.doubleScroll-scroll'
});
var _showScrollBar = function($self, options) {
if (options.onlyIfScroll && $self.get(0).scrollWidth <= Math.round($self.width())) {
// content doesn't scroll
// remove any existing occurrence...
$self.prev(options.topScrollBarWrapperSelector).remove();
return;
}
// add div that will act as an upper scroll only if not already added to the DOM
var $topScrollBar = $self.prev(options.topScrollBarWrapperSelector);
if ($topScrollBar.length == 0) {
// creating the scrollbar
// added before in the DOM
$topScrollBar = $(options.topScrollBarMarkup);
$self.before($topScrollBar);
// apply the css
$topScrollBar.css(options.scrollCss);
$(options.topScrollBarInnerSelector).css("height", "20px");
$self.css(options.contentCss);
var scrolling = false;
// bind upper scroll to bottom scroll
$topScrollBar.bind('scroll.doubleScroll', function() {
if (scrolling) {
scrolling = false;
return;
}
scrolling = true;
$self.scrollLeft($topScrollBar.scrollLeft());
});
// bind bottom scroll to upper scroll
var selfScrollHandler = function() {
if (scrolling) {
scrolling = false;
return;
}
scrolling = true;
$topScrollBar.scrollLeft($self.scrollLeft());
};
$self.bind('scroll.doubleScroll', selfScrollHandler);
}
// find the content element (should be the widest one)
var $contentElement;
if (options.contentElement !== undefined && $self.find(options.contentElement).length !== 0) {
$contentElement = $self.find(options.contentElement);
} else {
$contentElement = $self.find('>:first-child');
}
// set the width of the wrappers
$(options.topScrollBarInnerSelector, $topScrollBar).width($contentElement.outerWidth());
$topScrollBar.width($self.width());
$topScrollBar.scrollLeft($self.scrollLeft());
}
return this.each(function() {
var $self = $(this);
_showScrollBar($self, options);
// bind the resize handler
// do it once
if (options.resetOnWindowResize) {
var id;
var handler = function(e) {
_showScrollBar($self, options);
};
$(window).bind('resize.doubleScroll', function() {
// adding/removing/replacing the scrollbar might resize the window
// so the resizing flag will avoid the infinite loop here...
clearTimeout(id);
id = setTimeout(handler, options.timeToWaitForResize);
});
}
});
}
}( jQuery ));
document.addEventListener("DOMContentLoaded", () => {
django.jQuery('.results').doubleScroll({resetOnWindowResize: true});
})
3. Restart Django server
Maybe python manage.py collectstatic is required.
Press F12 in your browser, make sure the .js file is loaded correctly.

Related

turn.js display option based on window width

i am trying to make a flipbook with turn.js which is awesome.
the only problem i have is that i am trying to make it so in mobies it is single page display and in desktops double page display.
it does have the option to choose when creating the flipbook in javascript
display: 'single' or display: 'double'
i managed to achive changing that when you resize the window but with the onresize jwuery event but that makes it so it triggers only when you resize the window but if you dont it is always double page...so when the browser renders the page for mobile it is as defaul double page and not single
let me post my code here
// Create the flipbook
flipbook.turn({
// Magazine width
width: 922,
// Magazine height
height: 600,
// Duration in millisecond
duration: 1000,
// Enables gradients
gradients: true,
// Auto center this flipbook
autoCenter: true,
// Elevation from the edge of the flipbook when turning a page
elevation: 50,
// The number of pages
pages: 12,
// Events
when: {
turning: function(event, page, view) {
var book = $(this),
currentPage = book.turn('page'),
pages = book.turn('pages');
// Update the current URI
Hash.go('page/' + page).update();
// Show and hide navigation buttons
disableControls(page);
},
turned: function(event, page, view) {
disableControls(page);
$(this).turn('center');
$('#slider').slider('value', getViewNumber($(this), page));
if (page==1) {
$(this).turn('peel', 'br');
}
},
missing: function (event, pages) {
// Add pages that aren't in the magazine
for (var i = 0; i < pages.length; i++)
addPage(pages[i], $(this));
}
}
});
//change from single to double page
$(window).resize(function(){
var win = $(this); //this = window
if (win.width() >= 820) { flipbook.turn('display','double');}
else {
flipbook.turn('display','single');
}
});
i hope someone can help me fix this
To make the flipbook responsive for mobile, you can add the following code, which checks if the user agent of the navigator is mobile or not.
function checkMobile() {
return /iPhone|iPad|iPod|Android/i.test(navigator.userAgent);
}
And then use this in the resize of the window function.
if (!checkMobile()) { // not mobile
$('.flipbook').turn('display', 'double');
}
else {
$('.flipbook').turn('display', 'single');
}
You can use the above snippet after you have initialized the flipbook, to dynamically set the display (double, single) of the flipbook.
so hello everyone again, i managed to figure this out..
i changed the last code part from this
$(window).resize(function(){
var win = $(this); //this = window
if (win.width() >= 820) { flipbook.turn('display','double');}
else {
flipbook.turn('display','single');
}
});
to that
$(window).width(function(){
var win = $(this); //this = window
if (win.width() >= 820) { flipbook.turn('display','double');}
else {
flipbook.turn('display','single');
}
});
$(window).resize(function(){
var win = $(this); //this = window
if (win.width() >= 820) { flipbook.turn('display','double');}
else {
flipbook.turn('display','single');
}
});
it works fine both when you refresh the page and when you resize the window. I don't know if this the right way to do it but it seems to work perfectly fine
For mobile screens you have to change the screen width
here is tested and working code
if(window.innerWidth<768 && window.innerWidth >= 320) {
$('#flipbook').turn({
width:430,
height:650,
elevation:50,
inclination:50,
display: 'single',
autocenter:true,
acceleration: true,
gradients:true,
zoom:2,// you can change it as you desire
duration:50,
});
}
Here is what works for me on the mobile screen:
$('#flipbook').turn({
display: 'single',
acceleration: true,
gradients: true,
elevation:50,
when: {
turned: function(e, page) {
console.log('Current view: ', $(this).turn('view'));
}
}
});

Scroll event on ExtJS Grid Panel?

I have a grid panel in ExtJS with scroll bars. I am trying to detect when the user has scrolled all the way down(so that they can not move bar anymore). So far I have this, which detects when scroll occurs but provides no information(?) about where the scroll bar is.
//bufferedGrid is a grid panel
this.randomGrid.getView().on('scroll', this.onRandomGridScroll, this);
.
.
.
onRandomGridScroll : function(e, t)
{
console.log(e);
console.log(t);
}
Any pointers would be appreciated.
You can access the current scroll bar position(actually, the top of the scroll bar) and the maximum scroll position as follows(works in Firefox but not Chrome):
onBufferedGridScroll : function(e, t)
{
var max = this.bufferedGrid.getView().el.dom.scrollTopMax;
var current = this.bufferedGrid.getView().el.dom.scrollTop;
if( current == max )
alert('You have reached the bottom of the scroll !');
}
Add event on init
After grid is rendered add a mouseup event and a wheel down event.
'container #gridId':{
afterrender: this.addScrollEventListener
}
addScrollEventListener: function(comp){
comp.getTargetEl().on('mouseup', function(e, t) {
var height = comp.getTargetEl().getHeight();
if (height + t.scrollTop >= t.scrollHeight) {
}
});
comp.getTargetEl().on('wheeldown', function(e, t) {
var height = comp.getTargetEl().getHeight();
if (height + t.scrollTop >= t.scrollHeight) {
}
});
}

Using links within a parallax ScrollMagic site

I'm making a vertical parallax scrolling site with ScrollMagic which includes a navigation menu at the top to link within the site.
The menu itself works correctly when no parallax animation is applied to the scroll but when the parallax is added (ie the 2nd section moves up over the intro section), it seems unable to take the reduction in overall height into account when moving to the section, so it overshoots.
Here is some code:
var site = {
smController : {},
init : function () {
site.setupScroll();
site.setupMainNavigation();
site.setupAnimation();
},
setupScroll : function () {
// init the smController
var controller = new ScrollMagic({
globalSceneOptions: {
triggerHook: "onLeave"
}
});
site.smController = controller;
},
setupMainNavigation : function () {
$('.menuclick').on('click', function (event) {
event.preventDefault();
var anchor = $(this),
sectionId = $(anchor.attr('href'));
site.scrollToSection(sectionId);
});
},
/**
* uses tweenlite and scrolltoplugin from greensock
* #param {string} sectionId id of section to scroll to
* #return {void}
*/
scrollToSection : function (sectionId) {
var scrollYPos = $(sectionId).offset().top;
TweenLite.to(window, 0.5, { scrollTo:{ y: scrollYPos } });
},
setupAnimation : function () {
// parallax animation - move marginTop back by 100%
var tween = new TimelineMax()
.to('#section1', 2, { marginTop: '-100%', ease:Linear.easeNone });
var controller = site.smController,
scene = new ScrollScene({ duration: 500 })
.setTween(tween)
.addTo(controller);
// show indicators (requires debug extension)
scene.addIndicators();
}
};
$(document).ready(function () {
site.init();
});
Does anyone have a strategy to deal with moving (parallax) sections like this please?
Thanks
In ScrollMagic 1.1 you can now provide custom scroll functions AND scroll to the beginning of a specific scene.
Read more here:
http://janpaepke.github.io/ScrollMagic/docs/ScrollMagic.html#scrollTo
I would also strongly suggest not to use animated elements as scroll targets, because their position might be different before and after initiating scroll.
If you have elements that influence the DOM height, try to take them out of the DOM flow.
You can do this for example by adding an element as a placeholder and setting your element as positioned absolutely.
hope this helps.
J
I did something like this, using a similar setup to the demo page
new ScrollMagic.Scene(settings)
.setPin(slides[i])
.on('enter', function () {
var $trigger = $(this.triggerElement()),
$nextSlide = $trigger.parent().next().find('.slide');
/*
* If there's a next slide,
* update the href of the button
* to target the next slide
* otherwise, we're at the end;
* toggle the button state so it targets
* the top of the page
*/
if ($nextSlide.length) {
$('.btn-scroll').attr('href', '#' + $nextSlide.attr('id'));
} else {
$('.btn-scroll').attr('href', '#').addClass('up');
}
})
.on('leave', function (event) {
var $trigger = $(this.triggerElement()),
$firstSlide = $('.slide:first');
/*
* If we're going back up and we pass
* the first slide, update the button
* so it targets the first slide
*/
if (event.scrollDirection === 'REVERSE' && ($trigger.offset().top === $firstSlide.offset().top)) {
$('.btn-scroll').attr('href', originalTarget).removeClass('up');
}
})
.addTo(controller);
It just needs an anchor link with the href set to the first slide.
and something like this to handle the scroll:
var scrollToContent = function (target, speed) {
if (target === '#') {
target = $('body');
} else {
target = $(target);
}
speed = typeof speed !== 'undefined' ? speed : 'slow';
$('html, body').animate({
scrollTop: target.offset().top
}, speed);
}

scrolling pane using jquery

<script language="javascript">
$(document).ready(function($) {
var methods = {
init: function(options) {
this.children(':first').stop();
this.marquee('play');
},
play: function() {
var marquee = this,
pixelsPerSecond = 100,
firstChild = this.children(':first'),
totalHeight = 0,
difference,
duration;
// Find the total height of the children by adding each child's height:
this.children().each(function(index, element) {
totalHeight += $(element).innerHeight();
});
// The distance the divs have to travel to reach -1 * totalHeight:
difference = totalHeight + parseInt(firstChild.css('margin-top'), 10);
// The duration of the animation needed to get the correct speed:
duration = (difference/pixelsPerSecond) * 1000;
// Animate the first child's margin-top to -1 * totalHeight:
firstChild.animate(
{ 'margin-top': -1 * totalHeight },
duration,
'linear',
function() {
// Move the first child back down (below the container):
firstChild.css('margin-top', marquee.innerHeight());
// Restart whole process... :)
marquee.marquee('play');
}
);
},
pause: function() {
this.children(':first').stop();
}
};
$.fn.marquee = function(method) {
// Method calling logic
if (methods[method]) {
return methods[method].apply(this, Array.prototype.slice.call(arguments, 1));
} else if (typeof method === 'object' || !method) {
return methods.init.apply(this, arguments);
} else {
$.error('Method ' + method + ' does not exist on jQuery.marquee');
}
};
})(jQuery);
var marquee = $('#marquee');
marquee.marquee();
marquee.hover(function() {
marquee.marquee('pause');
}, function() {
marquee.marquee('play');
});
</script>
<style type="text/css">
#marquee {
margin:inherit;
width:auto;
height:inherit
}
</style>
I would like to create a scroller using jquery but I fail. The above code is the marquee I use to scroll up my items. And I am using it as below,
<html>
<body>
<div class="content">
<div id="marquee">
<ul>
<li>...</li>
....
</ul>
</div>
</div></body>
</html>
But it doesn't scroll at all, is there something incorrect in the code I am using you can find for me ?
Not sure if margin-top should work for this at all.
Try using position:relative for holder block(marquee) and position:absolute for content (ul). And update top instead of margin top. But in this case you may need to specify height and overflow:hidden for marquee div. Another options is to set height and oveflow:hidden for marquee, but leave position default. And scroll content using scrollTop or with some similar jquery functions.

Multiple functions and callbacks in a custom jquery plugin?

Okay I'm not sure if I'm going about this in the right way or not, but here goes...
I'm writing a custom jQuery plugin to provide drop menu functionality with animation (and as a learning exercise so please no "Why not just use superduperwonderplugin-x").
I want to be able to animate the menu in different ways depending on user options (i.e fade, slide, drop etc.). At the moment each different animation is handled by a separate function within the plugin file but I'm not sure how to handle the callback functions (passing this back)!
-- i.e. The animation is only happening on the last element in the object's stack.
Here's my code:
/**
* Grizzly's Menuifier
*
* #author Chris.Leaper
* #version a1.0
*/
(function($){
$.fn.gmenu = function(options) {
/* Transitions:
- fade >> fadeIn / fadeOut
- slide >> slideDown / slideUp
- drop >> (different to above?)
- bounce >> (custom config transition=? and easing=bounce)
- stretch >> (custom config transition=? and easing=elastic)
- fold >> (custom) drop menu # half width then 'fold' out to full menu width
*/
// Set the plugin default options:
var defaults = {
levels: '1',
fit: 'auto',
easing: 'linear',
transition: 'slide',
speed: 500
};
options = $.extend(defaults, options); // Merge the user options with the plugin defaults
return this.each(function() {
var $this = $(this);
var opt = options;
var container;
var ul;
// Setup the container elements (parent DIV/NAV and UL)!
if( $this.is('ul') ) container = $(this).parent();
else container = $(this);
console.log('Container: ' + container.get(0).tagName + ' id=#' + container.attr('id') + ' class=' + container.attr('class'));
ul = container.children('ul:first-child');
console.log('UL: ' + ul);
// Set the UL's position to relative:
if($(ul).css('position') != 'relative') $(ul).css('position', 'relative');
var offset;
var menus = ul.children('li:has(ul)');
console.log('List Item: ' + menus);
menus.each(function(index, menu) {
$menu = $(menu);
console.log('Menu: ' + $menu);
// Set the menu LI's position to relative (contains the absolutely positioned child UL!)
if($menu.css('position') != 'relative') $menu.css('position', 'relative');
// Get the menu LI's position relative to the document (it's offset)
// -- This is only needed when positioning non-child elements
// (i.e. ID linked menu>submenu relationships as may be used for a separated DIV based menu!!)
// offset = menu.offest();
// Position the submenu according to it's parent
var submenu = $menu.children('ul');
console.log('Submenu: ' + submenu.get(0).tagName + ' id=#' + submenu.attr('id') + ' class=' + submenu.attr('class'));
setPosition(submenu, $menu.height());
switch(opt.transition) {
case 'bounce':
setSMBounce(menu, opt);
break;
case 'fade':
setSMFade(menu, opt);
break;
case 'fold':
setSMFold(menu, opt);
break;
case 'stretch':
setSMStretch(menu, opt);
break;
case 'slide':
default:
menu = setSMSlide(menu, opt);
}
});
debug(this);
});
};
})(jQuery);
function setPosition(submenu, height) {
$(submenu).css({
left: 0,
position: 'absolute',
top: height
}).hide();
}
function setSMSlide(menu, opt) {
$menu = $(menu);
console.log('SM Slide: ' + $menu.get(0));
$menu.first('a').mouseenter(function() {
console.log('Start SlideDown');
$menu.stop(true, true).slideDown(opt.speed, opt.easing);
console.log('Stop SlideDown');
});
$menu.first('a').mouseleave(function() {
console.log('Start SlideUp');
$menu.stop(true, true).slideUp(opt.speed, opt.easing);
console.log('Stop SlideUp');
});
}
I think that I should be using a (same) namespace based approach to defining my separate functions (object literal or something?) but I wasn't sure what this meant or how to do it.
Can anyone help me please?
To encapsulate your functions setPosition and setSMSlide (in your example) just define them inside your plugin function (the good place would be after definition of default variable. It would look something like that:
var defaults = {
levels: '1',
fit: 'auto',
easing: 'linear',
transition: 'slide',
speed: 500
},
setPosition = function(submenu, height) {...},
setSMSlide = function(menu, opt) {...};
Because of the way the scoping in Javascript works your setPosition and setSMSlide functions will be still accessible from inside of your gmenu declaration.

Categories