This is my first attempt at a plugin but I think I'm missing the whole "How to" on this.
Ok here goes:
Trying to write an error popup box for form validation.
I like the look and functionality on this JavaScript code on this page, See demo here and source here.
It's basically what I want to do if the user enters invalid data.
Now I have tried to create a jQuery plugin with this code but it's not working, any help would be great :-)
(function($){
/* Might use the fadein fadeout functions */
var MSGTIMER = 20;
var MSGSPEED = 5;
var MSGOFFSET = 3;
var MSGHIDE = 3;
var errorBox = function(target, string, autohide, options)
{
var ebox = $(ebox);
var eboxcontent = $(eboxcontent);
var target = $(target);
var string = $(string);
var autohide = $(autohide);
var obj = this;
if (!document.getElementById('ebox')) {
ebox = document.createElement('div');
ebox.id = 'ebox';
eboxcontent = document.createElement('div');
eboxcontent.id = 'eboxcontent';
document.body.appendChild(ebox);
ebox.appendChild(eboxcontent);
ebox.style.filter = 'alpha(opacity=0)';
ebox.style.opacity = 0;
ebox.alpha = 0;
}
else {
ebox = document.getElementById('ebox');
eboxcontent = document.getElementById('eboxcontent');
}
eboxcontent.innerHTML = string;
ebox.style.display = 'block';
var msgheight = ebox.offsetHeight;
var targetdiv = document.getElementById(target);
targetdiv.focus();
var targetheight = targetdiv.offsetHeight;
var targetwidth = targetdiv.offsetWidth;
var topposition = topPosition(targetdiv) - ((msgheight - targetheight) / 2);
var leftposition = leftPosition(targetdiv) + targetwidth + MSGOFFSET;
ebox.style.top = topposition + 'px';
ebox.style.left = leftposition + 'px';
clearInterval(ebox.timer);
ebox.timer = setInterval("fadeMsg(1)", MSGTIMER);
if (!autohide) {
autohide = MSGHIDE;
}
window.setTimeout("hideMsg()", (autohide * 1000));
// hide the form alert //
this.hideMsg(msg) = function (){
var msg = document.getElementById('msg');
if (!msg.timer) {
msg.timer = setInterval("fadeMsg(0)", MSGTIMER);
}
};
// face the message box //
this.fadeMsg(flag) = function() {
if (flag == null) {
flag = 1;
}
var msg = document.getElementById('msg');
var value;
if (flag == 1) {
value = msg.alpha + MSGSPEED;
}
else {
value = msg.alpha - MSGSPEED;
}
msg.alpha = value;
msg.style.opacity = (value / 100);
msg.style.filter = 'alpha(opacity=' + value + ')';
if (value >= 99) {
clearInterval(msg.timer);
msg.timer = null;
}
else
if (value <= 1) {
msg.style.display = "none";
clearInterval(msg.timer);
}
};
// calculate the position of the element in relation to the left of the browser //
this.leftPosition(target) = function() {
var left = 0;
if (target.offsetParent) {
while (1) {
left += target.offsetLeft;
if (!target.offsetParent) {
break;
}
target = target.offsetParent;
}
}
else
if (target.x) {
left += target.x;
}
return left;
};
// calculate the position of the element in relation to the top of the browser window //
this.topPosition(target) = function() {
var top = 0;
if (target.offsetParent) {
while (1) {
top += target.offsetTop;
if (!target.offsetParent) {
break;
}
target = target.offsetParent;
}
}
else
if (target.y) {
top += target.y;
}
return top;
};
// preload the arrow //
if (document.images) {
arrow = new Image(7, 80);
arrow.src = "images/msg_arrow.gif";
}
};
$.fn.errorbox = function(options)
{
this.each(function()
{
var element = $(this);
// Return early if this element already has a plugin instance
if (element.data('errorbox')) return;
// pass options to plugin constructor
var errorbox = new errorBox(this, options);
// Store plugin object in this element's data
element.data('errorbox', errorbox);
});
};
})(jQuery);
How Im calling it
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
<title>jQuery Plugin - Error ToolTip</title>
<script type="text/javascript" src="js/jquery.js">
</script>
<script type="text/javascript" src="js/jquery.errorbox.js">
</script>
<script type="text/javascript">
$(document).ready(function(){
var name = document.getElementById('name');
if(name == "") {
$('#name','You must enter your name.',2).errorbox();
alert("Blank");
}
});
</script>
<link rel="stylesheet" type="text/css" href="css/errorbox.css" />
</head>
<body>
<div>
Name: <input type="text" id="name" width="30"></input>
</div>
</body>
Any help on my first plugin would be great, thanks in advance.
--Phill
The var errorBox = function(... needs to change to:
$.errorBox = function(...
then you can call it on the jquery object.
Secondly, for clarity you may want to use $('#eboxcontent') instead of document.getElementById('eboxcontent') . It wont be any faster, but it is "clearer" to other jquery developers.
Lastly, jQuery has many built in functions for fading things over a specified time period, and it appears that you have built your own. I know that jQuery's fading is cross-browser compatible. just use:
$('#someDivId').fadeOut(timeInMilliseconds);
var name = document.getElementById('name');
if(name == "") {
//...
}
should be
var name = document.getElementById('name');
if(name.value == "") {
//...
}
or
var name = $('#name').val();
if(name == "") {
//...
}
Related
I use prettyPhoto version: 3.1.6 to display simple lightbox with thumbnail.
Normally title attribute appear inside the lightbox for the active/selected image.
My client ask for this change
http://i.stack.imgur.com/7932x.jpg
How I can accomplish this?
Here is a part of my code
<a rel="prettyPhoto[pp_gal]"href="1.jpg" title="Staring at the sun"><img src="2.jpg"></a>
try this jquery function. You may have to style it a little.
(function($)
{
$.fn.avia_activate_lightbox = function(variables)
{
var defaults =
{
autolinkElements: 'a[rel^="prettyPhoto"], a[rel^="lightbox"], a[href$=jpg], a[href$=png], a[href$=gif], a[href$=jpeg], a[href$=".mov"] , a[href$=".swf"], a[href$=".flv"] , a[href*="vimeo.com"] , a[href*="youtube.com"]'
};
var options = $.extend(defaults, variables);
var imagedefaults =
{
autolinkImages: 'img[title!=""]'
};
return this.each(function()
{
var elements = $(options.autolinkElements, this),
lastParent = "",
counter = 0;
var images = $(imagedefaults.autolinkImages, this),
imgcounter = 0;
var alltitlesalt = new Array();
var alltitles = new Array();
images.each(function()
{
if($(this).attr('alt') != undefined && $(this).attr('alt') !="")
{
alltitlesalt.push($(this).attr('alt'));
}
else
{
alltitlesalt.push("");
};
alltitles.push($(this).attr('title'));
});
elements.each(function()
{
var el = $(this),
parentPost = el.parents('.post-entry:eq(0)'),
group = 'auto_group';
if(parentPost.get(0) != lastParent)
{
lastParent = parentPost.get(0);
counter ++;
}
if((el.attr('rel') == undefined || el.attr('rel') == '') && !el.hasClass('noLightbox'))
{
el.attr('rel','lightbox');
el.attr('title',alltitles[imgcounter]);
el.attr('alt',alltitlesalt[imgcounter]);
imgcounter ++;
}
});
if($.fn.prettyPhoto)
elements.prettyPhoto({ "theme": 'premium_photo', 'slideshow': 5000 }); /* facebook /light_rounded / dark_rounded / light_square / dark_square */
});
};
})(jQuery);
Reference
When I run the javascript code below, it load specified amount of images from Flickr.
By var photos = photoGroup.getPhotos(10) code, I get 10 images from cache.
Then, I can see the object has exactly 10 items by checking console.log(photos);
But actual image appeared on the page is less than 10 items...
I have no idea why this work this way..
Thank you in advance.
<html>
<head>
<script src="http://code.jquery.com/jquery-2.1.0.min.js"></script>
<script>
var PhotoGroup = function(nativePhotos, callback) {
var _cache = new Array();
var numberOfPhotosLoaded = 0;
var containerWidth = $("#contents").css('max-width');
var containerHeight = $("#contents").css('max-height');
$(nativePhotos).each(function(key, photo) {
$("<img src='"+"http://farm" + photo["farm"] + ".staticflickr.com/" + photo["server"] + "/" + photo["id"] + "_" + photo["secret"] + "_b.jpg"+"'/>")
.attr("alt", photo['title'])
.attr("data-cycle-title", photo['ownername'])
.load(function() {
if(this.naturalWidth >= this.naturalHeight) {
$(this).attr("width", containerWidth);
} else {
$(this).attr("height", containerHeight);
}
_cache.push(this);
if(nativePhotos.length == ++numberOfPhotosLoaded)
callback();
})
});
var getRandom = function(max) {
return Math.floor((Math.random()*max)+1);
}
this.getPhotos = function(numberOfPhotos) {
var photoPool = new Array();
var maxRandomNumber = _cache.length-1;
while(photoPool.length != numberOfPhotos) {
var index = getRandom(maxRandomNumber);
if($.inArray(_cache[index], photoPool))
photoPool.push(_cache[index]);
}
return photoPool;
}
}
var Contents = function() {
var self = this;
var contentTypes = ["#slideShowWrapper", "#video"];
var switchTo = function(nameOfContent) {
$(contentTypes).each(function(contentType) {
$(contentType).hide();
});
switch(nameOfContent) {
case("EHTV") :
$("#video").show();
break;
case("slideShow") :
$("#slideShowWrapper").show();
break;
default :
break;
}
}
this.startEHTV = function() {
switchTo("EHTV");
document._video = document.getElementById("video");
document._video.addEventListener("loadstart", function() {
document._video.playbackRate = 0.3;
}, false);
document._video.addEventListener("ended", startSlideShow, false);
document._video.play();
}
this.startSlideShow = function() {
switchTo("slideShow");
var photos = photoGroup.getPhotos(10)
console.log(photos);
$('#slideShow').html(photos);
}
var api_key = '6242dcd053cd0ad8d791edd975217606';
var group_id = '2359176#N25';
var flickerAPI = 'http://api.flickr.com/services/rest/?jsoncallback=?';
var photoGroup;
$.getJSON(flickerAPI, {
api_key: api_key,
group_id: group_id,
format: "json",
method: "flickr.groups.pools.getPhotos",
}).done(function(data) {
photoGroup = new PhotoGroup(data['photos']['photo'], self.startSlideShow);
});
}
var contents = new Contents();
</script>
</head>
<body>
<div id="slideShow"></div>
</body>
</html>
I fix your method getRandom() according to this article, and completely re-write method getPhotos():
this.getPhotos = function(numberOfPhotos) {
var available = _cache.length;
if (numberOfPhotos >= available) {
// just clone existing array
return _cache.slice(0);
}
var result = [];
var indices = [];
while (result.length != numberOfPhotos) {
var r = getRandom(available);
if ($.inArray(r, indices) == -1) {
indices.push(r);
result.push(_cache[r]);
}
}
return result;
}
Check full solution here: http://jsfiddle.net/JtDzZ/
But this method still slow, because loop may be quite long to execute due to same random numbers occurred.
If you care about performance, you need to create other stable solution. For ex., randomize only first index of your images sequence.
Hello Auto completion is not working well in my application.When we type a name it displays only a blank list[ Screenshots attached ].
Controller Code
public function list_UserByName($letters)
{
if(strpos($letters, ","))
{
$letters1 = explode(",",$letters);
$lecount = count($letters1);
$letters = $letters1[$lecount-1];
}
$letters = preg_replace("/[^a-z0-9 ]/si","",$letters);
$response=$this->user_model->getAutoUserList($letters);
}
Model Code
public function getAutoUserList($letters)
{
$letters = preg_replace("/[^a-z0-9 ]/si","",$letters);
//AND user_type='C' AND user_status='A'
$query="select * from gm_users where uname Like '%$letters%'";
$result_query =$this->db->query($query);
foreach($result_query->result() as $result)
{
//echo "###".$result."|";
//$pinlevel =$this->functions->get_pinlevel($result->pinLevel);
//echo $result->userId."###".$result->uname." [ ".$pinlevel." ] "."|";
echo $result->userId."###".$result->uname."".$result->address." ".$result->city."|";
}
}
billing.php
<input type="text" autocomplete="off" size="20" name="txtname" id="txtname" onkeyup="ajax_showOptions(this,'getCountriesByLetters',event);" value=""/>
ajax-dynamic-list.js
/************************************************************************************************************
(C) www.dhtmlgoodies.com, April 2006
This is a script from www.dhtmlgoodies.com. You will find this and a lot of other scripts at our website.
Terms of use:
You are free to use this script as long as the copyright message is kept intact. However, you may not
redistribute, sell or repost it without our permission.
Thank you!
www.dhtmlgoodies.com
Alf Magne Kalleland
************************************************************************************************************/
var ajaxBox_offsetX = 25;
var ajaxBox_offsetY = 5;
var ajax_list_externalFile = site_url+'/catalog/list_UserByName'; // Path to external file
var minimumLettersBeforeLookup = 1; // Number of letters entered before a lookup is performed.
var ajax_list_objects = new Array();
var ajax_list_cachedLists = new Array();
var ajax_list_activeInput = false;
var ajax_list_activeItem;
var ajax_list_optionDivFirstItem = false;
var ajax_list_currentLetters = new Array();
var ajax_optionDiv = false;
var ajax_optionDiv_iframe = false;
var ajax_list_MSIE = false;
if(navigator.userAgent.indexOf('MSIE')>=0 && navigator.userAgent.indexOf('Opera')<0)ajax_list_MSIE=true;
var currentListIndex = 0;
function ajax_getTopPos(inputObj)
{
var returnValue = inputObj.offsetTop;
while((inputObj = inputObj.offsetParent) != null){
returnValue += inputObj.offsetTop;
}
return returnValue;
}
function ajax_list_cancelEvent()
{
return false;
}
function ajax_getLeftPos(inputObj)
{
var returnValue = inputObj.offsetLeft;
while((inputObj = inputObj.offsetParent) != null)returnValue += inputObj.offsetLeft;
return returnValue;
}
// Edited
function ajax_option_setValue_bkp(e,inputObj)
{
if(!inputObj)inputObj=this;
var tmpValue = inputObj.innerHTML;
//alert(inputObj.id);
document.getElementById('saleUserId').value=inputObj.id;
if(ajax_list_MSIE)tmpValue = inputObj.innerText;else tmpValue = inputObj.textContent;
if(!tmpValue)tmpValue = inputObj.innerHTML;
val = ajax_list_activeInput.value.split(',');
vals = '';
count = val.length - 1;
for(i=0;i<count;i++)
{
vals = vals + val[i] + ',';
}
ajax_list_activeInput.value = vals + tmpValue;
if(document.getElementById(ajax_list_activeInput.name + '_hidden'))document.getElementById(ajax_list_activeInput.name + '_hidden').value = inputObj.id;
ajax_options_hide();
}
function ajax_option_setValue(e,inputObj)
{
if(!inputObj)inputObj=this;
var tmpValue = inputObj.innerHTML;
//alert(inputObj.id);
document.getElementById('saleUserId').value=inputObj.id;
if(ajax_list_MSIE)tmpValue = inputObj.innerText;else tmpValue = inputObj.textContent;
if(!tmpValue)tmpValue = inputObj.innerHTML;
ajax_list_activeInput.value = tmpValue;
if(document.getElementById(ajax_list_activeInput.name + '_hidden'))document.getElementById(ajax_list_activeInput.name + '_hidden').value = inputObj.id;
ajax_options_hide();
}
function ajax_options_hide()
{
if(ajax_optionDiv)ajax_optionDiv.style.display='none';
if(ajax_optionDiv_iframe)ajax_optionDiv_iframe.style.display='none';
}
function ajax_options_rollOverActiveItem(item,fromKeyBoard)
{
if(ajax_list_activeItem)ajax_list_activeItem.className='optionDiv';
item.className='optionDivSelected';
ajax_list_activeItem = item;
if(fromKeyBoard){
if(ajax_list_activeItem.offsetTop>ajax_optionDiv.offsetHeight){
ajax_optionDiv.scrollTop = ajax_list_activeItem.offsetTop - ajax_optionDiv.offsetHeight + ajax_list_activeItem.offsetHeight + 2 ;
}
if(ajax_list_activeItem.offsetTop<ajax_optionDiv.scrollTop)
{
ajax_optionDiv.scrollTop = 0;
}
}
}
function ajax_option_list_buildList(letters,paramToExternalFile)
{
ajax_optionDiv.innerHTML = '';
ajax_list_activeItem = false;
if(ajax_list_cachedLists[paramToExternalFile][letters.toLowerCase()].length<=1){
ajax_options_hide();
return;
}
ajax_list_optionDivFirstItem = false;
var optionsAdded = false;
for(var no=0;no<ajax_list_cachedLists[paramToExternalFile][letters.toLowerCase()].length;no++){
if(ajax_list_cachedLists[paramToExternalFile][letters.toLowerCase()][no].length==0)continue;
optionsAdded = true;
var div = document.createElement('DIV');
var items = ajax_list_cachedLists[paramToExternalFile][letters.toLowerCase()][no].split(/###/gi);
if(ajax_list_cachedLists[paramToExternalFile][letters.toLowerCase()].length==1 && ajax_list_activeInput.value == items[0]){
ajax_options_hide();
return;
}
div.innerHTML = items[items.length-1];
div.id = items[0];
div.className='optionDiv';
div.onmouseover = function(){ ajax_options_rollOverActiveItem(this,false) }
div.onclick = ajax_option_setValue;
if(!ajax_list_optionDivFirstItem)ajax_list_optionDivFirstItem = div;
ajax_optionDiv.appendChild(div);
}
if(optionsAdded){
ajax_optionDiv.style.display='block';
if(ajax_optionDiv_iframe)ajax_optionDiv_iframe.style.display='';
ajax_options_rollOverActiveItem(ajax_list_optionDivFirstItem,true);
}
}
function ajax_option_list_showContent(ajaxIndex,inputObj,paramToExternalFile,whichIndex)
{
if(whichIndex!=currentListIndex)return;
var letters = inputObj.value;
var content = ajax_list_objects[ajaxIndex].response;
var elements = content.split('|');
//alert(content);
ajax_list_cachedLists[paramToExternalFile][letters.toLowerCase()] = elements;
ajax_option_list_buildList(letters,paramToExternalFile);
}
function ajax_option_resize(inputObj)
{
ajax_optionDiv.style.top = (ajax_getTopPos(inputObj) + inputObj.offsetHeight + ajaxBox_offsetY) + 'px';
ajax_optionDiv.style.left = (ajax_getLeftPos(inputObj) + ajaxBox_offsetX) + 'px';
if(ajax_optionDiv_iframe){
ajax_optionDiv_iframe.style.left = ajax_optionDiv.style.left;
ajax_optionDiv_iframe.style.top = ajax_optionDiv.style.top;
}
}
function ajax_showOptions(inputObj,paramToExternalFile,e)
{
document.getElementById('saleUserId').value='';
if(e.keyCode==13 || e.keyCode==9)return;
if(ajax_list_currentLetters[inputObj.name]==inputObj.value)return;
if(!ajax_list_cachedLists[paramToExternalFile])ajax_list_cachedLists[paramToExternalFile] = new Array();
ajax_list_currentLetters[inputObj.name] = inputObj.value;
if(!ajax_optionDiv){
ajax_optionDiv = document.createElement('DIV');
ajax_optionDiv.id = 'ajax_listOfOptions';
document.body.appendChild(ajax_optionDiv);
if(ajax_list_MSIE){
ajax_optionDiv_iframe = document.createElement('IFRAME');
ajax_optionDiv_iframe.border='0';
ajax_optionDiv_iframe.style.width = ajax_optionDiv.clientWidth + 'px';
ajax_optionDiv_iframe.style.height = ajax_optionDiv.clientHeight + 'px';
ajax_optionDiv_iframe.id = 'ajax_listOfOptions_iframe';
document.body.appendChild(ajax_optionDiv_iframe);
}
var allInputs = document.getElementsByTagName('INPUT');
for(var no=0;no<allInputs.length;no++){
if(!allInputs[no].onkeyup)allInputs[no].onfocus = ajax_options_hide;
}
var allSelects = document.getElementsByTagName('SELECT');
for(var no=0;no<allSelects.length;no++){
allSelects[no].onfocus = ajax_options_hide;
}
var oldonkeydown=document.body.onkeydown;
if(typeof oldonkeydown!='function'){
document.body.onkeydown=ajax_option_keyNavigation;
}else{
document.body.onkeydown=function(){
oldonkeydown();
ajax_option_keyNavigation() ;}
}
var oldonresize=document.body.onresize;
if(typeof oldonresize!='function'){
document.body.onresize=function() {ajax_option_resize(inputObj); };
}else{
document.body.onresize=function(){oldonresize();
ajax_option_resize(inputObj) ;}
}
}
if(inputObj.value.length<minimumLettersBeforeLookup){
ajax_options_hide();
return;
}
ajax_optionDiv.style.top = (ajax_getTopPos(inputObj) + inputObj.offsetHeight + ajaxBox_offsetY) + 'px';
ajax_optionDiv.style.left = (ajax_getLeftPos(inputObj) + ajaxBox_offsetX) + 'px';
if(ajax_optionDiv_iframe){
ajax_optionDiv_iframe.style.left = ajax_optionDiv.style.left;
ajax_optionDiv_iframe.style.top = ajax_optionDiv.style.top;
}
ajax_list_activeInput = inputObj;
ajax_optionDiv.onselectstart = ajax_list_cancelEvent;
currentListIndex++;
if(ajax_list_cachedLists[paramToExternalFile][inputObj.value.toLowerCase()]){
ajax_option_list_buildList(inputObj.value,paramToExternalFile,currentListIndex);
}else{
var tmpIndex=currentListIndex/1;
ajax_optionDiv.innerHTML = '';
var ajaxIndex = ajax_list_objects.length;
ajax_list_objects[ajaxIndex] = new sack();
var search_key = inputObj.value.replace(" ","+");
//search_key1 = search_key.replace(",",",");
var url = ajax_list_externalFile + '/' +search_key;
ajax_list_objects[ajaxIndex].requestFile = url; // Specifying which file to get
ajax_list_objects[ajaxIndex].onCompletion = function(){ ajax_option_list_showContent(ajaxIndex,inputObj,paramToExternalFile,tmpIndex); }; // Specify function that will be executed after file has been found
ajax_list_objects[ajaxIndex].runAJAX(); // Execute AJAX function
}
}
function wordcount(string) {
var a = string.split(/\s+/g); // split the sentence into an array of words
return a.length;
}
function ajax_option_keyNavigation(e)
{
if(document.all)e = event;
if(!ajax_optionDiv)return;
if(ajax_optionDiv.style.display=='none')return;
if(e.keyCode==38){ // Up arrow
if(!ajax_list_activeItem)return;
if(ajax_list_activeItem && !ajax_list_activeItem.previousSibling)return;
ajax_options_rollOverActiveItem(ajax_list_activeItem.previousSibling,true);
}
if(e.keyCode==40){ // Down arrow
if(!ajax_list_activeItem){
ajax_options_rollOverActiveItem(ajax_list_optionDivFirstItem,true);
}else{
if(!ajax_list_activeItem.nextSibling)return;
ajax_options_rollOverActiveItem(ajax_list_activeItem.nextSibling,true);
}
}
/*if(e.keyCode==13 || e.keyCode==9){ // Enter key or tab key
if(ajax_list_activeItem && ajax_list_activeItem.className=='optionDivSelected')ajax_option_setValue(false,ajax_list_activeItem);
if(e.keyCode==13)return false; else return true;
}
if(e.keyCode==27){ // Escape key
ajax_options_hide();
}*/
}
//document.documentElement.onclick = autoHideList;
function autoHideList(e)
{
if(document.all)e = event;
if (e.target) source = e.target;
else if (e.srcElement) source = e.srcElement;
if (source.nodeType == 3) // defeat Safari bug
source = source.parentNode;
if(source.tagName.toLowerCase()!='input' && source.tagName.toLowerCase()!='textarea')ajax_options_hide();
}
Am a beginner in php as well as Codeigniter
Just echo your data in your controller
change
$response=$this->user_model->getAutoUserList($letters);
To
echo $this->user_model->getAutoUserList($letters);
change
onkeyup="ajax_showOptions(this,'getCountriesByLetters',event);
to
onkeyup="ajax_showOptions(this,'list_UserByName',event);
there is a question on this topic on stackoverflow, but an entire different process.
My Codeigniter autocomplete with ajax
I have hacked this tab system together.
I need to reduce the spacing(padding) between each tab. When viewed in firebug, you can see the javascript function is adding various left pixels between each, but instead of random padding-left pixels I need padding-left: 100px between each tab. Any idea how I can do that?
Below is the javascript.
<script type="text/javascript">
var menuAlignment = 'left'; // Align menu to the left or right?
var topMenuSpacer = 0; // Horizontal space(pixels) between the main menu items
var activateSubOnClick = false; // if true-> Show sub menu items on click, if false, show submenu items onmouseover
var leftAlignSubItems = true; // left align sub items t
var activeMenuItem = false; // Don't change this option. It should initially be false
var activeTabIndex = 0; // Index of initial active tab (0 = first tab) - If the value below is set to true, it will override this one.
var rememberActiveTabByCookie = true; // Set it to true if you want to be able to save active tab as cookie
var MSIE = navigator.userAgent.indexOf('MSIE')>=0?true:false;
var Opera = navigator.userAgent.indexOf('Opera')>=0?true:false;
var navigatorVersion = navigator.appVersion.replace(/.*?MSIE ([0-9]\.[0-9]).*/g,'$1')/1;
/*
These cookie functions are downloaded from
http://www.mach5.com/support/analyzer/manual/html/General/CookiesJavaScript.htm
*/
function Get_Cookie(name) {
var start = document.cookie.indexOf(name+"=");
var len = start+name.length+1;
if ((!start) && (name != document.cookie.substring(0,name.length))) return null;
if (start == -1) return null;
var end = document.cookie.indexOf(";",len);
if (end == -1) end = document.cookie.length;
return unescape(document.cookie.substring(len,end));
}
// This function has been slightly modified
function Set_Cookie(name,value,expires,path,domain,secure) {
expires = expires * 60*60*24*1000;
var today = new Date();
var expires_date = new Date( today.getTime() + (expires) );
var cookieString = name + "=" +escape(value) +
( (expires) ? ";expires=" + expires_date.toGMTString() : "") +
( (path) ? ";path=" + path : "") +
( (domain) ? ";domain=" + domain : "") +
( (secure) ? ";secure" : "");
document.cookie = cookieString;
}
function showHide()
{
if(activeMenuItem){
activeMenuItem.className = 'inactiveMenuItem';
var theId = activeMenuItem.id.replace(/[^0-9]/g,'');
document.getElementById('submenu_'+theId).style.display='none';
var img = activeMenuItem.getElementsByTagName('IMG');
if(img.length>0)img[0].style.display='none';
}
var img = this.getElementsByTagName('IMG');
if(img.length>0)img[0].style.display='inline';
activeMenuItem = this;
this.className = 'activeMenuItem';
var theId = this.id.replace(/[^0-9]/g,'');
document.getElementById('submenu_'+theId).style.display='block';
if(rememberActiveTabByCookie){
Set_Cookie('dhtmlgoodies_tab_menu_tabIndex','index: ' + (theId-1),100);
}
}
function initMenu()
{
var mainMenuObj = document.getElementById('mainMenu');
var menuItems = mainMenuObj.getElementsByTagName('A');
if(document.all){
mainMenuObj.style.visibility = 'hidden';
document.getElementById('submenu').style.visibility='hidden';
}
if(rememberActiveTabByCookie){
var cookieValue = Get_Cookie('dhtmlgoodies_tab_menu_tabIndex') + '';
cookieValue = cookieValue.replace(/[^0-9]/g,'');
if(cookieValue.length>0 && cookieValue<menuItems.length){
activeTabIndex = cookieValue/1;
}
}
var currentLeftPos = 1;
for(var no=0;no<menuItems.length;no++){
if(activateSubOnClick)menuItems[no].onclick = showHide; else menuItems[no].onmouseover = showHide;
menuItems[no].id = 'mainMenuItem' + (no+1);
if(menuAlignment=='left')
menuItems[no].style.left = currentLeftPos + 'px';
else
menuItems[no].style.right = currentLeftPos + 'px';
currentLeftPos = currentLeftPos + menuItems[no].offsetWidth + topMenuSpacer;
var img = menuItems[no].getElementsByTagName('IMG');
if(img.length>0){
img[0].style.display='none';
if(MSIE && !Opera && navigatorVersion<7){
img[0].style.bottom = '-1px';
img[0].style.right = '-1px';
}
}
if(no==activeTabIndex){
menuItems[no].className='activeMenuItem';
activeMenuItem = menuItems[no];
var img = activeMenuItem.getElementsByTagName('IMG');
if(img.length>0)img[0].style.display='inline';
}else menuItems[no].className='inactiveMenuItem';
if(!document.all)menuItems[no].style.bottom = '-1px';
if(MSIE && navigatorVersion < 6)menuItems[no].style.bottom = '-2px';
}
var mainMenuLinks = mainMenuObj.getElementsByTagName('A');
var subCounter = 1;
var parentWidth = mainMenuObj.offsetWidth;
while(document.getElementById('submenu_' + subCounter)){
var subItem = document.getElementById('submenu_' + subCounter);
if(leftAlignSubItems){
// No action
}else{
var leftPos = mainMenuLinks[subCounter-1].offsetLeft;
document.getElementById('submenu_'+subCounter).style.paddingLeft = LeftPos + 'px';
subItem.style.position ='absolute';
if(subItem.offsetWidth > parentWidth){
leftPos = leftPos - Math.max(0,subItem.offsetWidth-parentWidth);
}
subItem.style.paddingLeft = leftPos + 'px';
subItem.style.position ='static';
}
if(subCounter==(activeTabIndex+1)){
subItem.style.display='block';
}else{
subItem.style.display='none';
}
subCounter++;
}
if(document.all){
mainMenuObj.style.visibility = 'visible';
document.getElementById('submenu').style.visibility='visible';
}
document.getElementById('submenu').style.display='block';
}
window.onload = initMenu;
</script>
I don't want to put down your effort, but your solution looks very complicated, feels slow on a 3Ghz dual-core workstation, throws a lot of Javascript errors and, as your question illustrates, makes customization very difficult. I'm not sure this is the way to go - I find it hard to dig into the script to even find the place to make the change you want to make.
Why not, for example, use something pre-built and small like the old but mostly reliable framework-agnostic DOMTab?
There is a great number of tab scripts based on the popular frameworks as well, see e.g. here.
How do you go around adding an element to the DOM which still knows which instance of an object created it so that it can refer back to the class later on?
Basically the following code should allow you to add one or more photo albums to a page, where Javascript generates all the DOM elements needed.
I.e. in the following code example it would just take five lines of code (on top of the includes) - the HTML and three JS init lines.
If I add a button with the onclick event of pa.fwd() it pages to the correct element, but obviously I don't know what the "pa" variable will be. I have tried this - no use as it prefers to the div I am clicking on. I have tried parent, again no use as it travels up the DOM.
(i.e. what would I need to put in the next_btn.onclick line for it to execute pa.fwd() at run time, or pa2.fwd() or fotos.fwd() depending on the parent variable?)
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Photo Album Demo</title>
<style type="text/css">
/* CSS Style Declarations */
</style>
<script type="text/javascript">
// <![CDATA[
// Noddy Functions
function showDiv(divname)
{
document.getElementById(divname).style.display = "block";
document.getElementById(divname).style.visibility = "visible";
}
function hideDiv(divname)
{
document.getElementById(divname).style.display = "none";
document.getElementById(divname).style.visibility = "hidden";
}
/* -- [[ Photo Album Class ]] -- */
var kjsPhotoAlbum_prev_button = "/libs/prev.png";
var kjsPhotoAlbum_next_button = "/libs/next.png";
function kjsPhotoAlbum(_container_id,_width,_height)
{
var photos = new Array();
var width = _width;
var height = _height;
var parent_container = _container_id;
var imageCache = new Array();
var photo_index = 0;
var obj = function(id) { return document.getElementById(id); }
this.addPhoto = function(_filename,_caption)
{
photos[photos.length] = new Object({fn:_filename,c:_caption});
imageCache[photos.length-1] = new Image();
imageCache[photos.length-1].src = _filename;
}
this.initalise = function()
{
obj(parent_container).className = 'kjsPhotoAlbum';
obj(parent_container).style.width = width + "px";
obj(parent_container).style.height = height + "px";
var caption_bar = document.createElement("div");
caption_bar.id = parent_container + "_caption";
caption_bar.className = 'kjsPhotoAlbumCaption';
caption_bar.style.position = "absolute";
document.getElementById(parent_container).appendChild(caption_bar);
var image_box = document.createElement("img");
image_box.id = parent_container + "_image";
image_box.className = 'kjsPhotoAlbumImage';
image_box.style.position = "absolute";
image_box.src = '/libs/1px.gif';
document.getElementById(parent_container).appendChild(image_box);
var next_btn = document.createElement("div");
next_btn.id = parent_container + "_next";
next_btn.className = 'kjsPhotoAlbumNext';
next_btn.style.position = "absolute";
next_btn.onmousemove = function(e) { showDiv(parent_container + "_next_image"); }
next_btn.onmouseout = function(e) { hideDiv(parent_container + "_next_image"); }
next_btn.onclick = function(e) { this.fwd(); }
/* HOW DO I GET THIS TO KNOW THAT IT WAS GENERATED FROM THE PA */
document.getElementById(parent_container).appendChild(next_btn);
var next_btn_img = document.createElement("img");
next_btn_img.id = parent_container + "_next_image";
next_btn_img.className = 'kjsPhotoAlbumNextImage';
next_btn_img.style.position = "absolute";
next_btn_img.src = kjsPhotoAlbum_next_button;
document.getElementById(next_btn.id).appendChild(next_btn_img);
var prev_btn = document.createElement("div");
prev_btn.id = parent_container + "_prev";
prev_btn.className = 'kjsPhotoAlbumPrev';
prev_btn.style.position = "absolute";
document.getElementById(parent_container).appendChild(prev_btn);
var prev_btn_img = document.createElement("img");
prev_btn_img.id = parent_container + "_prev_image";
prev_btn_img.className = 'kjsPhotoAlbumPrevImage';
prev_btn_img.style.position = "absolute";
prev_btn_img.src = kjsPhotoAlbum_prev_button;
document.getElementById(prev_btn.id).appendChild(prev_btn_img);
}
this.play = function()
{
if (photos.length > 0)
{
this.show(0);
}
else
{
alert("Unable to play, no photos exist");
}
}
this.resizeImage =function(idx)
{
var ratio = 1;
var new_w = width;
var new_h = height;
var src_w = imageCache[idx].width;
var src_h = imageCache[idx].height;
ratio = Math.min( new_w / src_w, new_h / src_h );
new_w = ratio * src_w;
new_h = ratio * src_h;
obj(parent_container + "_image").style.width = new_w + "px";
obj(parent_container + "_image").style.height = new_h + "px";
obj(parent_container + "_image").style.marginLeft = (0-(new_w/2)) + "px";
obj(parent_container + "_image").style.marginTop = (0-(new_h/2)) + "px";
}
this.show = function()
{
obj(parent_container + "_image").src = photos[photo_index].fn;
obj(parent_container + "_caption").innerHTML = "<span>" + photos[photo_index].c + "</span>";
this.resizeImage(photo_index);
}
this.fwd = function()
{
if (photo_index < photos.length)
{
photo_index++;
this.show(photo_index);
}
}
this.initalise();
}
/* -- [[ End Class ]] ---------------------------------------------------- */
var pa;
function init()
{
pa = new kjsPhotoAlbum('PhotoAlbum',800,500);
pa.addPhoto("/libs/1.jpg","Caption 1");
pa.addPhoto("/libs/2.jpg","Caption 2");
pa.addPhoto("/libs/3.jpg","Caption 3");
pa.addPhoto("/libs/4.jpg","Caption 4");
pa.addPhoto("/libs/5.jpg","Caption 5");
}
// ]]>
</script>
</head>
<body onload="init()">
<div id="PhotoAlbum"></div>
</body>
</html>
You need to capture the instance of kjsPhotoAlbum (available as this) inside a variable, and reference that variable inside your click handler code:
var that = this;
next_btn.onclick = function(e) { that.fwd(); }
This is a JavaScript closure.