Jquery mega drop down loading after page loads - javascript

There may not be a fix for this. I am using a jquery drop down menu that loads when the DOM is ready. From what I understand this means it waits until the page is fully loaded until it becomes ready to be used.
This is problematic for a menu system because people want to use the menu right away often before the entire page is loaded.
Here is my site where you can see this happening.
http://bit.ly/g1sn5t
This is my script that I am using for the menu
$(document).ready(function() {
function megaHoverOver(){
$(this).find(".sub").stop().fadeTo('fast', 1).show();
//Calculate width of all ul's
(function($) {
jQuery.fn.calcSubWidth = function() {
rowWidth = 0;
//Calculate row
$(this).find("ul").each(function() {
rowWidth += $(this).width();
});
};
})(jQuery);
if ( $(this).find(".row").length > 0 ) { //If row exists...
var biggestRow = 0;
//Calculate each row
$(this).find(".row").each(function() {
$(this).calcSubWidth();
//Find biggest row
if(rowWidth > biggestRow) {
biggestRow = rowWidth;
}
});
//Set width
$(this).find(".sub").css({'width' :biggestRow});
$(this).find(".row:last").css({'margin':'0'});
} else { //If row does not exist...
$(this).calcSubWidth();
//Set Width
$(this).find(".sub").css({'width' : rowWidth});
}
}
function megaHoverOut(){
$(this).find(".sub").stop().fadeTo('fast', 0, function() {
$(this).hide();
});
}
var config = {
sensitivity: 2, // number = sensitivity threshold (must be 1 or higher)
interval: 0, // number = milliseconds for onMouseOver polling interval
over: megaHoverOver, // function = onMouseOver callback (REQUIRED)
timeout: 0, // number = milliseconds delay before onMouseOut
out: megaHoverOut // function = onMouseOut callback (REQUIRED)
};
$("ul#topnav li .sub").css({'opacity':'0'});
$("ul#topnav li").hoverIntent(config);
});
function clearText(field){
if (field.defaultValue == field.value) field.value = '';
else if (field.value == '') field.value = field.defaultValue;
}
// JavaScript Document
Is there anyway to get this to load before everything else?

You can put those scripts wherever you whant in the case of understanding exactly what you are doing.
HTML are rendered sequentially, so scripts cannot get the DOM object or js variables defined later in the document. That's why we usually use document onload event to do the init thing since all elements are loaded.
In this case, I guess you can probably put the scripts without document.ready right after the closing of ul#topnav tag.

Related

Creating a class based JS instead of id

So I have created this javascript that animates a certain place using it's ID.
The problem is that there are many of those on the site and meaning this I'd have to duplicate this function a lot of times just to replace the x in getElementById("x").
So here is the code I fully done by myself:
var popcount = 0;
var opanumber = 1;
var poptimeout;
function pop() {
if (popcount < 10) {
popcount++;
if (opanumber == 1) {
document.getElementById("nav1").style.opacity = 0;
opanumber = 0;
poptimeout = setTimeout("pop()", 50);
}
else {
document.getElementById("nav1").style.opacity = 1;
opanumber = 1;
poptimeout = setTimeout("pop()", 50);
}
}
else {
popcount = 0;
document.getElementById("nav1").style.opacity = 1;
}
}
function stoppop() {
clearTimeout(poptimeout);
popcount = 0;
document.getElementById("nav1").style.opacity = 1;
}
I would gladly appreciate any information on how I could solve this situation and also any tutorials about using classes and "this".
Something like this; rather than hard code a value into a function it is better to pass the value in so you can reuse the function on more than one thing. In this case you can now call startPop and stopPop with the name of a CSS class.
var popTimeout;
function setOpacity(className, value) {
Array.prototype.forEach.call(
document.getElementsByClassName(className),
function(el) {
el.style.opacity = value;
}
);
}
function pop(className, popCount, opaNumber) {
if (popCount < 10) { //Must be even number so you end on opacity = 1
setOpacity(className, opaNumber);
popTimeout = setTimeout(function() {
pop(className, popCount++, 1-opaNumber);
}, 50);
}
}
function startPop(className) {
pop(className, 0, 0);
}
function stopPop(className) {
clearTimeout(popTimeout);
setOpacity(className, 1);
}
In case you are wondering about the 1 - opaNumber; this is a simpler way of switching a value between 1 and 0. As 1-1=0 and 1-0=1.
Well you started out with recognizing where you have the problem and that's already a good thing :)
To make your code a bit more compact, and get as many things as possible out of the local scope, you could check the following implementation.
It is in a sense a small demo, where I tried adding as much comments as possible.
I edited a bit more after realizing you rather want to use classnames instead of id's :) As a result, I am now rather using the document.querySelectorAll that gives you a bit more freedom.
Now you can call the startPop function with any valid selector. If you want to pop purely on ID, you can use:
startPop('#elementId');
or if you want to go for classes
startPop('.className');
The example itself also add's another function, nl trigger, that shows how you can start / stop the functions.
I also opted to rather use the setInterval method instead of the setTimeout method. Both callback a function after a certain amount of milliseconds, however setInterval you only have to call once.
As an extra change, stopPop also now uses the document.querySelectorAll so you have the same freedom in calling it as the startPop function.
I added 2 more optional parameters in the startPop function, namely total and callback.
Total indicates the maximum times you wish to "blink" the element(s), and the callback provides you with a way to get notified when the popping is over (eg: to update potential elements that started the popping)
I changed it a bit more to allow you to use it for hovering over an element by using the this syntax for inline javascript
'use strict';
function getElements( className ) {
// if it is a string, assume it's a selector like #id or .className
// if not, assume it's an element
return typeof className === "string" ? document.querySelectorAll( className ) : [className];
}
function startPop(className, total, callback) {
// get the element once, and asign a value
var elements = getElements( className ),
current = 0;
var interval = setInterval(function() {
var opacity = ++current % 2;
// (increase current and set style to the left over part after dividing by 2)
elements.forEach(function(elem) { elem.style.opacity = opacity } );
// check if the current value is larger than the total or 10 as a fallback
if (current > (total || 10)) {
// stops the current interval
stopPop(interval, className);
// notifies that the popping is finished (if you add a callback function)
callback && callback();
}
}, 50);
// return the interval so it can be saved and removed at a later time
return interval;
}
function stopPop(interval, className) {
// clear the interval
clearInterval(interval);
// set the opacity to 1 just to be sure ;)
getElements( className ).forEach(function(elem) {
elem.style.opacity = 1;
});
}
function trigger(eventSource, className, maximum) {
// get the source of the click event ( the clicked button )
var source = eventSource.target;
// in case the attribute is there
if (!source.getAttribute('current-interval')) {
// start it & save the current interval
source.setAttribute('current-interval', startPop(className, maximum, function() {
// completed popping ( set the text correct and remove the interval )
source.removeAttribute('current-interval');
source.innerText = 'Start ' + source.innerText.split(' ')[1];
}));
// change the text of the button
source.innerText = 'Stop ' + source.innerText.split(' ')[1];
} else {
// stop it
stopPop(source.getAttribute('current-interval'), className);
// remove the current interval
source.removeAttribute('current-interval');
// reset the text of the button
source.innerText = 'Start ' + source.innerText.split(' ')[1];
}
}
<div class="nav1">
test navigation
</div>
<div class="nav2">
Second nav
</div>
<div class="nav1">
second test navigation
</div>
<div class="nav2">
Second second nav
</div>
<a id="navigation-element-1"
onmouseover="this.interval = startPop( this )"
onmouseout="stopPop( this.interval, this )">Hover me to blink</a>
<button type="button" onclick="trigger( event, '.nav1', 100)">
Start nav1
</button>
<button type="button" onclick="trigger( event, '.nav2', 100)">
Start nav2
</button>
If you do want to take it back to using IDs, then you will need to think about popTimeout if you run this on more than one element at a time.
function setOpacity(id, value) {
document.getElementById(id).style.opacity = value;
}
function runPop(id) {
function pop(id, popCount, opaNumber) {
if (popCount < 10) { //Must be even number so you end on opacity = 1
setOpacity(id, opaNumber);
popTimeout = setTimeout(function() {
pop(id, popCount++, 1-opaNumber);
}, 50);
}
}
var popTimeout;
pop(id, 0, 0);
return function() {
clearTimeout(popTimeout);
setOpacity(id, 1);
}
}
var killPop = [];
function startPop(id) {
killPop[id] = runPop(id);
}
function stopPop(id) {
killPop[id]();
}

Jquery using $this in a for loop in an each loop

Here's my code for a basic jquery image slider. The problem is that I want to have many sliders on one page, where each slider has a different number of images. Each slider has the class .portfolio-img-container and each image .portfolio-img.
Basic html setup is as follows:
<div class="portfolio-item"><div class="portfolio-img-container"><img class="portfolio-img"><img class="portfolio-img"></div></div>
<div class="portfolio-item"><div class="portfolio-img-container"><img class="portfolio-img"><img class="portfolio-img"></div></div>
And javascript:
$.each($('.portfolio-img-container'), function(){
var currentIndex = 0,
images = $(this).children('.portfolio-img'),
imageAmt = images.length;
function cycleImages() {
var image = $(this).children('.portfolio-img').eq(currentIndex);
images.hide();
image.css('display','block');
}
images.click( function() {
currentIndex += 1;
if ( currentIndex > imageAmt -1 ) {
currentIndex = 0;
}
cycleImages();
});
});
My problem comes up in the function cycleImages(). I'm calling this function on a click on any image. However, it's not working: the image gets hidden, but "display: block" isn't applied to any image. I've deduced through using devtools that my problem is with $(this). The variable image keeps coming up undefined. If I change $(this) to simply $('.portfolio-img'), it selects every .portfolio-img in every .portfolio-img-container, which is not what I want. Can anyone suggest a way to select only the portfolio-imgs in the current .portfolio-img-container?
Thanks!
this within cycleImages is the global object (I'm assuming you're not using strict mode) because of the way you've called it.
Probably best to wrap this once, remember it to a variable, and use that, since cycleImages will close over it:
$.each($('.portfolio-img-container'), function() {
var $this = $(this); // ***
var currentIndex = 0,
images = $this.children('.portfolio-img'), // ***
imageAmt = images.length;
function cycleImages() {
var image = $this.children('.portfolio-img').eq(currentIndex); // ***
images.hide();
image.css('display', 'block');
}
images.click(function() {
currentIndex += 1;
if (currentIndex > imageAmt - 1) {
currentIndex = 0;
}
cycleImages();
});
});
Side note:
$.each($('.portfolio-img-container'), function() { /* ... */ });
can more simply and idiomatically be written:
$('.portfolio-img-container').each(function() { /* ... */ });
Side note 2:
In ES2015 and above (which you can use with transpiling today), you could use an arrow function, since arrow functions close over this just like other functions close over variables.
You can't just refer to this inside of an inner function (see this answer for a lot more detailed explanations):
var self = this; // put alias of `this` outside function
function cycleImages() {
// refer to this alias instead inside the function
var image = $(self).children('.portfolio-img').eq(currentIndex);
images.hide();
image.css('display','block');
}

Load content in div from a href tag in jQuery

I want to load all images before displaying them in a slideshow. I have been searching a lot but nothing seems to work. This is what i have so far and it doesn't work. I am loading images from the <a> tag from another div.
$('.slideshow').load(function(){
if (loaded<length){
first = $(settings.thumb).eq(loaded).find('a').attr("href");
$('<img src="'+first1+'"/>').appendTo('.slideshow');
}
else{ $('.slideshow').show(); }
loaded++;
});
Add an event listener to each image to respond to when the browser has finished loading the image, then append it to your slideshow.
var $images = $("#div_containing_images img");
var numImages = $images.length;
var numLoaded = 0;
var $slideshow = $(".slideshow");
$images.each(function() {
var $thisImg = $(this);
$thisImg.on("load", function() {
$thisImg.detach().appendTo($slideshow);
numLoaded++;
if (numLoaded == numImages) {
$slideshow.show();
}
});
});
It's a good idea to also listen for the error event as well, in case the image fails to load. That way you can increase numLoaded to account for broken image. Otherwise, your slideshow will never be shown in the event the image is broken.
Also note, that by calling detach() followed by appendTo() I am am moving the image in the DOM. If instead, you want to copy the image, use clone() instead of detach().
* EDIT TO MODIFY USER'S EXACT USE CASE *
var $images = $("li.one_photo a");
var numImages = $images.length;
var numLoaded = 0;
$images.each(function() {
$('<img />',
{ src: $(this).attr("href") })
.appendTo('.slideshow')
.on("load error", function() {
numLoaded++;
if(numLoaded == numImages) {
$('.slideshow').show();
}
});
});
* EDIT #2 *
Just realized you were putting everything in the $(".slideshow").load() function. Since $(".slideshow") represents a DIV, it will never raise a load event, and the corresponding function will never execute. Edited above accordingly.

Display & Hide Divs Sequentially on click

Basics:
I have a webpage which loads several content divs to a page, then shows them sequentially when the user clicks on the "load more" link.
Example here: JSFiddle Example
$("#load-more").click(function(e) {
var t = $(this);
if (t.data('disabled')){
return;
}
var hiddenGroups = $('.group:not(:visible)');
if (hiddenGroups.length > 0){
hiddenGroups.first().show();
} else{
t.data('disabled', true);
}
});
I have two questions based on this example:
1) The "load more" link should hide its self when the final div is showing, but is not functioning correctly in my example.
however, I'd really like it to:
2) Hide the "load more" div, and instead show/change the functionality to "Hide Items" and hide the content divs in reverse order upon click.
This is how I would do it:
JSFIDDLE
The idea:
Set a variable instructing if you are toggling more or less
var load = 'more';
check if we are loading more or less
and a function to switch the direction and the button text, to stop copy/pasting the same javascript.
Although there are only two things happening in the function, i'm working on the assumption this might grow as your application grows - if this was not to grow then it may be worth getting rid of the function andjust adding the code with the rest
The full snippet:
var load = 'more';
$("#load-more").on('click',function (e) {
// show the next hidden div.group, then disable load more once all divs have been displayed
// if we are loading more
if (load == 'more') {
// get the amount of hidden groups
var groups = $('.group:not(:visible)');
// if there are some available
if (groups.length > 0) {
// show the first
groups.first().show();
// if that was the last group then set to load less
if (groups.length == 1) {
switchLoadTo('less');
}
}
// we are loading less
} else {
// get the groups which are currently visible
var groups = $('.group:visible');
// if there is more than 1 (as we dont want to hide the initial group)
if (groups.length > 1) {
// hide the last avaiable
groups.last().hide();
// if that was the only group available, set to load more
if (groups.length == 2) {
switchLoadTo('more');
}
}
}
});
function switchLoadTo(dir) {
load = dir;
$("#load-more").html('Load ' + dir);
}
http://jsfiddle.net/8Re3t/20/
See snippet below. Probably not the most optimized code, but you get the idea and can optimize it on implementation.
var load = true;
$("#load-more").click(function(e) {
// show the next hidden div.group, then disable load more once all divs have been displayed
var t = $(this);
if (t.data('disabled')){
return;
}
var hiddenGroups = $('.group:not(:visible)');
if(load) {
hiddenGroups.first().show();
} else {
$('.group:visible').last().hide();
}
if (hiddenGroups.length > 1){
jQuery("#load-more").html("Load");
load = true;
} else {
jQuery("#load-more").html("Hide");
load = false;
}
});

Body onload in Javascript

I had written one JS in asp.net. I had called that from body onload, but the JS doesn't get called where I have put my debugger. What could be possible reasons for this? I'm developing website in dotnetnuke.
The JS I have written is syntactically and logically correct.
<script type="text/javascript">
var displayTime, speed, wait, banner1, banner2, link1, link2, bannerIndex, bannerLocations, bannerURLs;
function initVar() {
debugger;
displayTime = 10; // The amount of time each banner will be displayed in seconds.
speed = 5; // The speed at which the banners is moved (1 - 10, anything above 5 is not recommended).
wait = true;
banner1 = document.getElementById("banner1");
banner2 = document.getElementById("banner2");
//link1 = document.getElementById("link1");
//link2 = document.getElementById("link2");
//banner1 = document.getElementById("banner1");
//banner2 = document.getElementById("banner2");
banner1.style.left = 0;
banner2.style.left = 500;
bannerIndex = 1;
/* Important: In order for this script to work properly, please make sure that the banner graphic and the
URL associated with it have the same index in both, the bannerLocations and bannerURLs arrays.
Duplicate URLs are permitted. */
// Enter the location of the banner graphics in the array below.
//bannerLocations = new Array("internet-lg.gif","jupiterweb.gif","jupitermedia.gif");
bannerLocations = new Array("image00.jpg", "image01.jpg", "image02.jpg", "admin_ban.bmp");
// Enter the URL's to which the banners will link to in the array below.
bannerURLs = new Array("http://www.internet.com","http://www.jupiterweb.com","http://www.jupitermedia.com");
}
function moveBanner() {
//debugger;
if(!wait){
banner1.style.left = parseInt(banner1.style.left) - (speed * 5);
banner2.style.left = parseInt(banner2.style.left) - (speed * 5);
if(parseInt(banner1.style.left) <= -500){
banner1.style.left = 500;
bannerIndex = (bannerIndex < (bannerLocations.length - 1)) ? ++bannerIndex :0;
banner1.src = bannerLocations[bannerIndex];
//link1.href = bannerURLs[bannerIndex];
wait = true;
}
if(parseInt(banner2.style.left) <= -500){
banner2.style.left = 500;
bannerIndex = (bannerIndex < (bannerLocations.length - 1)) ? ++bannerIndex :0;
banner2.src = bannerLocations[bannerIndex];
//link2.href = bannerURLs[bannerIndex];
wait = true;
}
setTimeout("moveBanner()",100);
} else {
wait = false;
setTimeout("moveBanner()", displayTime * 1000);
}
}
</script>
REGISTRATION IN JS
<body onload="initVar(); moveBanner();">
</body>
I ran your code. Both methods executed without me having to make any modifications to the posted code. Is there possibly some other code that is overwriting the onload method?
The DotNetNuke best practice for binding to the "onload" property in JavaScript is to hook into JQuery's ready() method:
jQuery(document).ready( function() {
// put your code here
initVar();
moveBanner();
});
DotNetNuke 4.9.x and later ship with the jQuery JavaScript library included.
Have you edited DNN's Default.aspx? Otherwise, there isn't any way for you to have access to the body tag to add the onload attribute like you show.
How are you injecting this script? Are you using a Text/HTML module, are you using the Page Header Text setting for the page, are you adding it directly to the skin, have you written a custom module, or something else?
Instead of using the onload attribute on the body tag, I would suggest wiring up to that event in the script itself. If you're using any code to inject the script, you can ask DNN to register jQuery or a ScriptManager (for ASP.NET AJAX) so that you can use those libraries to wire the event up easily. If you can't guarantee that those are on the page, use the following:
function addLoadEvent(func) {
var oldonload = window.onload;
if (typeof window.onload != 'function') {
window.onload = func;
} else {
window.onload = function() {
if (oldonload) {
oldonload();
}
func();
}
}
}
addLoadEvent(function () {
initVar();
moveBanner();
});
I don't know much about asp.net but if you can put javascript code in your page, then you can try this alternative:
window.onload = function()
{
// any code here
}
This is the same as what you put in body tag.
The CSS left property takes a length, not an integer. You must have units for non-zero lengths. (Even when setting it using JavaScript!).

Categories

Resources