I want to add a toolbar button before the firefox search container in my addon. But it is completely clearing my navigation bar.
I suspect the offending code is due to an empty array or something but i cant be certain.
//insert before search container
if(navBar && navBar.currentSet.indexOf("mybutton-id")== -1 )//navBar exist and our button doesnt
{
var arrayCurrentSet= navBar.currentSet.split(',');
var arrayFinalSet= [];//empty at first
if(arrayCurrentSet.indexOf("search-container") != -1)//if search-container exists in current set
{
// check item by item in current set
var i= null;
while(i=arrayCurrentSet.shift() != undefined)
{
if(i == "search-container")//"search-container" found !!
{
/*insert our button after it but only if our button does not already exist*/
if(arrayFinalSet.indexOf("mybutton-id") == -1) arrayFinalSet.push("mybutton-id");
}
arrayFinalSet.push(i);
dump("arrayFinalSet "+ i);
}
}
else //damn search-container doesnt exist
{
arrayFinalSet= arrayCurrentSet;
arrayFinalSet.push("mybutton-id");//add our button to the end of whatever is available in nav bar
}
//set new navBar
navBar.currentSet= arrayFinalSet.join(',');
}
The full code is available
https://builder.addons.mozilla.org/addon/1052494/latest/
http://jsfiddle.net/CQ4wA/
I'm not too sure why the navigation bar has been removed, but I think it would be better to approach this from a different angle. Rather than messing around with an array of strings, try using DOM methods instead.
e.g.
var sC=navBar.querySelector("#search-container");
navBar.insertBefore(btn, sC);
The code you have here seems to work - but the toolbar needs to find your button somehow. Your current code doesn't even insert the button into the document, meaning that the toolbar has no chance to find it by its ID. It should be in the toolbar palette palette however, the palette also determines which buttons the user can choose from when customizing the toolbar. So you probably want to do something like this first:
var toolbox = navBar.toolbox;
toolbox.palette.appendChild(btn);
You might also want to simplify your code:
var arrayCurrentSet = navBar.currentSet.split(',');
var insertionPoint = arrayCurrentSet.indexOf("search-container");
if (insertionPoint >= 0)
arrayCurrentSet.splice(insertionPoint, 0, "mybutton-id");
else
arrayCurrentSet.push("mybutton-id");
navBar.currentSet = arrayCurrentSet.join(',');
And finally, you probably want to make the browser remember the current button set, it doesn't happen automatically:
document.persist(navBar.id, "currentset");
Note that the button that will be inserted into the toolbar is not the same as the button you added to the palette - the toolbar code clones the button, with one copy being left in the palette. So event listeners added via addEventListener will sadly be lost. It is better to use a command attribute and insert a <command> element into the document that you will attach your listener to.
Note: in XUL you usually want the command and not the click event - unless you are really interested in mouse clicks only and want to ignore the button being triggered by keyboard or other means.
Related
I have a webpage with images.
A user can click on images to show() or hyde() these images.
Sometimes, the user opens a popup to watch a video.
Then the code hide() all elements previously opened.
When the user closes the video, i need to know which elements was previously opened in order to show only them.
What is the best way to do that ?
What i've done :
I've created an array and i push images names into it.
var arr_popup_open = [];
Then, this function is called when user open a popup and hide all elements :
function toggleAllPopup() {
if( $('#popup_micro_1').is(":visible"))
{
$('#popup_micro_1').hide();
arr_popup_open.push('#popup_micro_1');
}
if( $('#popup_micro_2').is(":visible"))
{
$('#popup_micro_2').hide();
arr_popup_open.push('#popup_micro_2');
}
if( $('#popup_micro_3').is(":visible"))
{
$('#popup_micro_3').hide();
arr_popup_open.push('#popup_micro_3');
}
}
// and so on ... I have 7 images so it seems it's not very well optimized
When i need to show only images previously opened, i execute this code, a loop to show() elements in array.
$('#close_pop_up').click(function() {
for(var i= 0; i < arr_popup_open.length; i++)
{
$(arr_popup_open[i]).show();
}
});
What do you think about that ? Is there a better way to to do it ?
There are a few ways you could go about this with jQuery. Your way should work, but if you want to reduce the amount of code you could do something like:
var visibleDivs = $('div:visible', '#ContainerDiv');
Alternatively you could add a specific class to all visible elements when you show them and use:
var visibleDivs = $('.someClassName');
When hiding them due to your popup, you can store the list in the data of any element. In this case, putting it on #close_pop_up might make sense:
visibleDivs.hide();
$('#close_pop_up').data('myDivs', visibleDivs);
When you want to show them again in your click function:
$('#close_pop_up').click(function() {
$(this).data('myDivs').show();
});
Looks fine to me. Just remember to clear arr_popup_open in the start of the toggleopen function.
The alternative you could do if you really wanted is to keep the information of what is open or closed in Javascript variables that get updated when you open and close things. This way you don't need to depend on complex things such as is(:visible)
The Goal
So I have a list of <div>s all in a single column layout that have either the class "active" or "inactive". The active class shows a graphic to the right of the item and the inactive class doesn't. I have it setup so that hitting the up or down arrow key moves the "active" class (and the graphic with it) to the previous or next item. It isn't animated, but you can visually see the graphic disappearing and reappearing on the tag above or below.
Now I'd like to have the page scroll down on arrow keypress so that the top edge of the item is always in the same spot. Since the element list is larger than the page window, it's necessary to automatically scroll the browser so that the selected <div> is always in the center of the screen...
The Code
//Paging through items with arrow keys
theWindow.keydown(function (e) {
var key = e.keyCode,
oldItem = $('li.active')
if ((key === 40 && oldItem.next().length) || (key === 38 && oldItem.prev().length)) {
var theWindowMod = (window.innerHeight / 2) + 43,
theHTML = $('html'),
theDetail = $('.detail')
theHTML.addClass('notransition')
if (key === 40 && oldItem.next().length) {
oldItem.removeClass('active').next().addClass('active')
} else if (key === 38 && oldItem.prev().length) {
oldItem.removeClass('active').prev().addClass('active')
}
var newItem = $('li.active')
window.scroll(0, newItem.offset().top - theWindowMod)
e.preventDefault()
$('.detail-inner.active').fadeOut(0).removeClass('active')
$('section.active, .tab.active').removeClass('active')
newItem.find('.tab').add(theDetail).addClass('active')
theDetail.find('.detail-' + newItem.attr('class').split(' ')[0]).addClass('active').fadeIn(0)
setTimeout(function () {
theHTML.removeClass('notransition')
}, 1)
}
});
The Problem
The problem is that in all versions of Safari but no other browser, the window.scroll method is just a bit behind the CSS class switching performance wise. What happens is they end up in two different redraw events and it looks like the page is 'glitching' when you scroll down because you can briefly see the graphic to the right of the next element before the browser scrolls down.
The Live Demo
You can view it live here:
http://hashtag.ly/#minecraft
Use the arrow keys to page through items. Notice the jump. How should I go about resolving this?
I think a solution that avoids the issue is your best bet.
From a UX perspective, when I'm browsing a site, I don't like it when the site usurps control of the scroll position.
Also, for people with tall-ish browsers (like me) currently there can be a lot of white real-estate between the details and the selected post. (see screenshot)
My recommendation is to change the design so the details show up next to the selected post and let the user do the scrolling. Controlling the locaiton of the details with CSS, so they're next to the selected post, will put it in the same render-cycle as everything else.
The details being closer to the selected post might look something like this:
Update:
Come to think of it, AOL's email client Alto has the UX that you've implemented. In Alto, the left column does scroll automatically if you browse with the keys. But, you're not actually scrolling, they're adding content into the container element and bringing it into view (I forget what this is called... virtualization?). It looks like they're managing all the scroll-related visuals and behavior, themselves, and are not using the native functionality. So, it's all JS controlled CSS and DOM manipulation, there isn't actually a scrollTo() invocation. This puts it all in the same render cycle.
Try using a button instead of the div and use setfocus() when the specific item is activated. The browser will automatically scroll to make the focused button always visible. You can use CSS to make the button look exactly like the div.
I have a button on a banner at the top of my page that launches several yui2 overlays on to the screen. Each overlay has a close button on it (which just changes the visibility to hidden so it can be reused). After the overlays are launched, there is also a button on the banner that appears will close all overlays if clicked.
This gives the use the option to close all or close each one individually. This is what i am stuck on:
If the user closes an individual overlay, after I close the overlay, I want to check if any other overlay is still open. If they happen to have closed all of them individually, then I need to revert the banner at the top and remove the "close all button".
I can search for all overlays by doing a:
var elements = YAHOO.util.Dom.getElementsByClassName('test');
I cant think of the logic I would need to do to go through that array each time they close an overlay to see all of them are set to visibility if hidden. If so, then execute a function. If there is still any overlays visible on the page, then do nothing.
This is the answer I came up with. Just not sure if it is correct.
var elements = document.getElementsByClassName('test');
var visiblecounter = 0;
for (var i = 0; i < elements.length; i++) {
if(elements[i].style.visibility!='hidden'){
alert("not hidden");
visiblecounter ++;
}
}
if(visiblecounter > 0){
alert("all overlays are closed individually. you can remove close all button");
}
You mention you are reusing those overlays so since you are pooling the overlays for reuse, I assume you have them in an array or something like that. Instead of checking the DOM (which is always expensive) to see if they are visible or not, loop through the array of overlays checking the visible attribute, like:
var anyVisible = false;
for (i = 0; i < myOverlays.length; i+=1) {
anyVisible |= myOverlays[i].cfg.getProperty("visible");
}
If any of them are visible, disable the button.
I am not sure I get the question, but I will try my best to help. Here are some things I would do. I also define an active class, so my HTML elements would be written as this:
<div class="john active"></div>
and in my css I would write.
.john {display: none};
.active {display: block};
So by default the object is hidden! But when you append the "active" class to it, it appears on the screen. So now we can do the following wizardry.
$(".hideButton").click(function() {
$(this).removeClass("active");
});
If I want to hide all the other objects, assuming that they have the same parent in the DOM
$(".hideOthersButton").click(function() {
$(this).siblings().removeClass("active");
});
if I want to hide all objects that share the same parent.
$(".hideEverything").click(function() {
$(".parent").children().removeClass("active");
});
I hope this helps! let me know if you need more help. The solution uses Jquery but you can repurpose the logic for anything else.
Ran into problem with creating custom select dropdown plugin in jQuery. I'm at the one-at-the-time-open feature. Meaning, that when you open a dropdown, then other(s) will close.
My first idea was to create some global array with all dropdowns in it as objects. Then in the "opening"-function, I would add the first line to first check that none of the dropdowns are open (if open, then close them.)
I created a very scaled version of my script: http://jsfiddle.net/ngGGy/1/
Idea would be to have only one dropdown open at the time. Meaning, that when you open one, other(s) must be closed, if not they will automatically close when a new one is opened.
Your dropdown set seems to behave like an accordion.
This is easier to accomplish if you wrap each dropdown in a div with a class, then use that to target all the dropdown uls you have.
I forked your jsfiddle with a working example.
(EDIT updated fiddle link)
You can keep track of the DropDownSelectized lists like this: http://jsfiddle.net/pimvdb/ngGGy/3/.
(function($){
var lists = $(); // cache of lists
$.fn.DropDownSelect = function (settings) {
jQuery.globalEval("var zindex = 100");
var thiselement = $(this);
var thislist = thiselement.next('ul');
lists = lists.add(thislist); // add current one to cache
thiselement.click(function () {
lists.slideUp(); // hide all lists initially
if (thislist.is(':visible')) {
thislist.slideUp();
} else {
thislist.css('z-index', ++zindex).slideDown();
}
});
};
})(jQuery);
You're definitely on the right track, but if you're only going to have one dropdown list open at a time then you want them to be related somehow. Fortunately your markup is already there, so all we should have to do is modify the JS. I've updated your jsFiddle project here: http://jsfiddle.net/ninjascript/ngGGy/4/
First the selector. jQuery will let you select attributes that are similar by using ^= like this:
$('div[id^=button]').DropDownSelect();
Now we just have to update your plugin a bit. Notice that what used to be 'thislist' is now called 'everylist'. Now we can enforce that every list closes on click before opening the list associated with the button that was clicked.
(function($){
$.fn.DropDownSelect = function (settings) {
jQuery.globalEval("var zindex = 100");
var thiselement = $(this);
var everylist = thiselement.next('ul');
thiselement.click(function () {
var thislist = $(this).next('ul');
if (everylist.is(':visible')) {
everylist.slideUp();
}
thislist.css('z-index', ++zindex).slideDown();
});
};
})(jQuery);
Good luck!
Why not raise an event that all drop-downs subscribe to. pass in the id (or instance) of the one currently being opened. In the handler check whether the handling instance is the one being opened. If not, close it.
Good afternoon all
Here is my scenario:
I have user controls within a master page and within a user control, I may have an update panel. I have the following bit of code I place within a usercontrol that maintains various control styling during partial postback i.e. those controls affected within by an asp update panel.
function pageLoad(sender, args) {
if (args.get_isPartialLoad()) {
$("select, span, input").uniform();
Indeed, when an update panel does its thing, the Fancy Dan styling is maintained.
However, I have one gripe - when a 'large' partial postback occurs, occassionally you'll see the default, generic control styling reappear breifly and then the uniform kicks in to reapply the new fancy styles.
Is there any way I can avoid seeing the nasty old, default, bland stylings?
Any suggestions greatly appreciated.
Check out working with PageManagerRequests: MSDN Working With PageRequestManager
Sys.WebForms.PageLoadingEventArgs Class
Sys.WebForms.PageRequestManager pageLoading Event
$(function() {
Sys.WebForms.PageRequestManager.getInstance().add_pageLoading(beautify);
});
function beautify(sender, eventArgs) {
// If we have event args
if (eventArgs != null) {
// for each panel that is being updated, update the html by adding color red
// to select, span, input elements
// must remember to call .html() to put html back into the panelsUpdating[i], otherwise it puts in the jQuery Object
for (var i = 0; i < eventArgs.get_panelsUpdating().length; i++) {
//My test code
//var content = eventArgs._panelsUpdating[i].outerHTML;
//var jContent = $(content);
//$("input", jContent).css("color", "red");
//jContent = $('<div>').append(jContent)
//var jContentToContent = jContent.html();
//alert(jContentToContent);
//eventArgs._panelsUpdating[i].outerHTML = jContentToContent;
//Cleaned up
var jContent = $(eventArgs._panelsUpdating[i].outerHTML);
$("select, span, input", jContent).uniform();
jContent = $('<div>').append(jContent);
eventArgs._panelsUpdating[i].outerHTML = jContent.html();
}
}
}
Edit: I think you understood that the issue was the elements were being placed into the DOM (and therefore painted) before your javascript had a chance to make them uniform(). This intercepts the UpdatePanel and uniform()'s the code prior to it inserted into the DOM
Edit 2 Alright, I've updated it a bunch, I tested this with my test code there and then included the code you're likely to add. Also, I took a short cut in eventArgs._panelsUpdating - i should really be using the get and set functions, but this works.