jQuery JavaScript Detect object in view / window - javascript

Ok, this may have been asked dozens of times over. But I'm not exactly sure what technique or term I would have to search for in order to find out more information on this.
What I have is an Unordered list of Elements. Each one acting as a screen on a project I am working on. Each element is the size of the browser window, so its more like a power point presentation then anything if I were to describe it in any sense of the logic I have here. So what I am trying to do is find out when any particular slide is active in the view. One I have a few images I would like to load only when the view is active. But what I am really looking for is to highlight the navigation according to the slide I am on. The navigation is a fixed element so its always on the screen while everything else just floats on in, in a matter of speaking. Another reason I need this is, the navigation doesn't come into effect until the second slide. So I need a way to trigger the navigation into displaying itself only when every slide other than the first one is in view, and I will remove it when the first slide is in the view.
Hopefully I made some sense on this, anyone have any idea's?

try something like adding a class to the element in view when it enters view, removing all other instances of the same class when you put it in view.
something like
$('.active').removeClass('e_active');
$(this).addClass('e_active');
would enable you to find the current active element. alternatively if you are using element id's to find the element, you could do
$('#element').addClass('e_active')
where '#element' is the active element
if you are clicking elements to get the ID, you could use something like this:
HTML:
<ul class="slides">
<li id="elem1" class="e_active">content1</li>
<li id="elem2">content2</li>
<li id="elem3">content3</li>
<li id="elem4">content4</li>
</ul>
<ul class="nav">
<li data-link="elem1">Link1</li>
<li data-link="elem2">Link2</li>
<li data-link="elem3">Link3</li>
<li data-link="elem4">Link4</li>
</ul>
then with the script you could do something like this:
$('li', '.nav').click(function(){
elem = $(this).data('link');
$('.e_active').removeClass('e_active');
$('#'+elem, '.slides').addClass('e_active');
})
which would enable you to simply use .e_active in your stylesheet and any jquery code to alter the active element.
Of course, if you are doing it on scroll or keypress, the method is the same, you just need to attach the function to the relavent event handler.
also, with your 'navigation' issue, you just need to use $('.nav').hide() when the element id for the first slide also has the class e_active and $('.nav').show() it when it doesn't, adding and removing a class like nav_hidden from the nav element so it doesn't repeatedly show it.

Related

Inside of a event registered by NodeList.connect(), how to get a reference to the triggered node?

Short-form question
I'm executing dojo.query() and applying an event handler to all of the returned nodes, using the NodeList.onmouseenter() function. However, when one of those nodes triggers the event, I need to know which one it was, so I can traverse the DOM from that particular node's location. How can I get a reference that particular node at trigger-time?
Long-form question with full context
I am trying to adapt a jQuery-powered navigation menu (see overview and complete source) to work in a Dojo environment. In a nutshell, the HTML looks like this:
<ul class="topnav">
<li>Home</li>
<li>
Tutorials
<ul class="subnav"> <!-- This CSS class hides the <ul> on page load -->
<li>Sub Nav Link</li>
<li>Sub Nav Link</li>
</ul>
<span></span> <!-- CSS fills the <span> with an image -->
</li>
...
The outermost <ul> tag represents the navigation menu as a whole... and the nested <ul> tags are where a menu item has a drop-down submenu. Such menu items also have an <span> tag in there, to display an arrow image and make things easier to identify during DOM-traversing.
So, the jQuery code adds an event handler to all those <span> tags. The tutorial link above uses an "onClick", but I plan to change that to a hover (i.e. "onmouseenter" and "onmouseleave"). Either way, the event trigger causes the hidden submenu to be diplayed with a jQuery "slideDown" effect (equivalent to Dojo's "wipeIn").
$("ul.topnav li span").click(function() {
$(this).parent().find("ul.subnav").slideDown('fast').show();
});
To get from the <span> to the nested <ul>, the event handler crawls the DOM one level up and then one level back down... starting from $(this). This is the part that is killing me in Dojo! I've tried dozens of variations, but here is my current (broken) code:
dojo.require("dojo.fx");
dojo.require("dojo.NodeList-traverse");
dojo.require("dojo.NodeList-manipulate");
dojo.ready(function(){
dojo.query("ul.topnav li span").onmouseenter(function(node) {
node.siblings("ul.subnav").wipeIn().play();
});
}
Inside of Dojo's NodeList event connection functions (e.g. onmouseenter), I can't figure out how to get a trigger-time reference to the node that was triggered. The anonymous function I'm passing to "onmouseenter" takes a parameter called "node", but that's just my wishful thinking. Dojo doesn't really pass such a reference.
What I need is an equivalent to the $(this) on the second line of the above jQuery snippet. A reference to the particular node that was event-triggered, so that I can traverse the DOM from that particular node's location. Is there an easy way to do this with Dojo's NodeList that I'm just missing?
IIRC, the onmouseenter callback doesn't receive a node as a parameter but receives an event object instead. If this is the case you can try to:
Get the node through other means, (evt.currTarget or something like that, I always get confused with these...)
Use a forEach instead, get the references to the nodes, and do the event connection manually:
dojo.query('blabla').forEach(function(node){
dojo.connect(node, 'onmouseenter', function(evt){
//node should point to wat you want now
node.siblings("ul.subnav").wipeIn().play();
});
});
The best reference you can get to a node, is the node itself including its id.
At trigger time "this" is the node and with it you can get everything in particular its id with: this.id.
dojo.query(".anyClass").connect(Mouse.enter, function(evt){
var xId=this.id;
console.log("You entered element "+xId);
});

How can I make a sortable, changeable, addable, removable web-page element?

I'm creating a web-app interface which will allow users to construct sentences by clicking on words/phrases. Each word of phrase will be contained in its own page element, eg. a <div>. So the div class would need to allow:
dragging into a different order relative to the other divs/words (ie. sorting)
an X (only visible when hovering over) in the top right corner of its box to remove it completely upon clicking
changing of font/text upon click actions
be inserted into the page when the user wishes to add another word
For instance, imagine this is the page (quote marks denote an element):
"Hi, my name is" [Textbox] "I work at..." "and I was born in" [Combobox]
The phrase in italics is 'inactive' and not part of the sentence, but if the user clicked it then it would change to "I work at" [Textbox] and a new div would be added eg. "And also at..." to allow further expansion. The phrases in bold would have a close button /clickable action to get rid of them or make them inactive again. Also they would be draggable to change the order. As you can see I would also need to dynamically insert new textboxes and comboboxes to accommodate more phrases requiring input.
Could somebody give me a brief run down on what steps I need to take? I've seen sortable list elements in JQuery eg. http://jsfiddle.net/ctrlfrk/A4K4t/ which is a start. Would I just need to spruce this up with some basic JavaScript and CSS? Or would I need to use server-side scripting to dynamically add more page content?
To summarize, I need the div to change font/text upon clicking, have a hover-over close button, remove and be inserted upon simple click events, and be sortable by dragging. I also need comboboxes/textboxes to be added/removed in parallel.
Any tips would be greatly appreciated.
You don't need the server for new content unless it is stored or generated there.
Not having done this exact thing before I don't see any grand scheme to implement, I would just add the features incrementally until you get where you need to go. You may have to refactor things a bit along the way, but I probably don't have to tell you that. ;)
One thing you will have to do when you add or delete elements is to rebind the jQuery functions, it won't enough to just call them when the document loads. So put them in a function and call it whenever you add or remove an element.
Regarding your description of the UI behavior, you say that inactive elements can be dragged and that they can be made active by clicking on them. You can do this (just see if the thing has been dragged or not on mouseup to know whether it was drag or a click) but it might not be the best UI design choice, IMO. It wouldn't be the worst thing in the world, but I find it a little frustrating when the wrong thing happens when I try to do something. Of course, implementing and seeing for yourself is probably best.
Sorting:
I would implement the phrases in an unordered list (ul) which makes it as simple as
$("#ulId").sortable();
You can make ul/li items stack next to each other in a similar manner to how you make horizontal navigation menus out of ul elements.
A destrunction button:
Just use a template li similar to this
<li id="text1Wrapper">
<span id="text1"
onmouseover="javascript:$('text1Remove').fadeIn();"
onmouseout="javascript:$('text1Remove').fadeOut();"
onclick="javascript:$('text1Content').
replaceWith(
$(document.createElement('input'))
.attr('type','text')
.val($('text1Content').text())
);">
<span id="text1Content">Text Here</span>
<img id="text1Remove"
style="display: none;"
src="./x.jpg" alt="Remove"
onclick="javascript:$(this).parent().remove();"
/>
</span>
</li>
A quick description:
The span mouse over event tells jquery to fade in the remove button (so when you hover, the remove button becomes available).
The span mouse out event tells jquery to fade out the remove button when it's no longer needed (so when you move the mouse off the li, the remove button is no longer visible)
The onclick of the span replaces the span with a text box containing the content of the span. I will leave the "save changes" as an excercise for the reader, since it's essentially the same but in reverse.
Img tag style has display: none to hide it initially. This is essentially the end product of a fadeOut() but without the fade.
The on click event of the remove button gets the parent (the li) and removes it, and all children from the dom.
Note that the events are only put here because it seemed the logical place to explain it. All events in the outer SPAN tag are a useless waste of space, as they will all be overridden when we clone the node in the next section.
Insertion into the page:
All you have to do now is
var cloneLi = $('#text1Wrapper').clone();
cloneLi.attr('id', 'text2Wrapper');
var cloneSpan = cloneLi.children('#text1').attr('id', 'text2');
var cloneContent = cloneSpan.children('#text1Content').attr('id', 'text2Content');
var cloneRemove = cloneSpan.children('#text1Remove').attr('id', 'text2Remove');
You will then need to change the mouseover, mouseout and onclick functions of the outer span using jquery events:
cloneSpan.mouseover(function(e) {
// Insert functionality from template here
});
cloneSpan.mouseout(function(e) {
// Insert functionality from template here
});
cloneSpan.click(function(e) {
// Insert functionality from template here
});

Cloning li elements in JQuery

I'm using EasySlider, but unlike any of the examples of this plugin that I've found, I show multiple elements at any one time and the width is 100%. The separate images make up a full, long consecutive image, which is Photoshopped so even the borderline between first and last looks natural. It's configured to be a continuous slide automatically.
In the plugin there is this code:
if(options.continuous){
$("ul", obj).prepend($("ul li:last-child", obj).clone().css("margin-left","-"+ w +"px"));
$("ul", obj).append($("ul li:nth-child(2)", obj).clone());
$("ul", obj).css('width',(s+1)*w);
};
My issue is that only the first element is cloned after the last one, so upon the second rotation of this slider, only the first element is shown, until it gets to the far left, then the other images appear to "pop" in. (similar: [EXAMPLE] if you imagine and all images to the right of the fairground not appearing until the fairground gets to the far left).
Is there any better way to manage this cloning of elements so that ALL the images are cloned? or perhaps someone can think of a better way? I'm new to JQuery
NOTE: I'm trying to create an operation whereby as an element leaves the screen on the left, it is placed back onto the right. Is there a way to ultimately achieve this?
I think you may want to consider a different plugin.
If you change the plugin, updating will require reapplying your patches.
I'd recommend
http://www.gmarwaha.com/jquery/jcarousellite/
or
http://sorgalla.com/jcarousel/
These both support what you are talking about.
I agree with troynt, in this case it would be better to use something that meets your requirements.
I just made a simple "plugin" what only does, what you need, maybe it's useful to you.
http://jsfiddle.net/doktormolle/4c5tt/
You can setup delay and duration and choose to pause on hover.
Given you have a list of items in your scroller like this:
<ul>
<li>...</li>
<li>...</li>
<li>...</li>
<li>...</li>
</ul>
And assuming that every time you advance the scroller, the first-most li moves off screen, you can continuously pluck the first li off the front of the list and append it back to the end on each click of a "next" button or scroll event. If you're using jQuery >= 1.4, you can do this by using the detach() method, which will remove the element from the DOM but keep all its associated data so you can easily reattach it later:
$('ul li:first').detach().appendTo('ul');
Dont know, if I unterstand it the right way, you may try this:
//at first run add a class "init" to the inital li-elements,
//so that later only them will be cloned
if(!$('ul li.init',obj).length)$('ul li',obj).addClass('init');
//prepend clones of all li-elements with init-class and remove their init-class
$("ul", obj).prepend($("ul li.init", obj).clone().removeClass('init').css({}));

How do I delegate Jquery UI click from image to list item?

I have a UL that contains items that the user can click on and move from one list to another. I have a function to inspect the list item and move it from one list to another and it works well -- except for one problem.
The list item contains an image (which also responds to the click event). So if my user clicks on the image (essentially a + or - sign that represents add/remove) the function does not work, as the element that it expected as the target is the image, not the list item. How can I delegate the image click to the li that contains it?
The list item is built using struts2 iterator
<li class="ui-widget-content" id="<s:property value="userId"/>">
<img src="/images/add_16.png" border="0" alt="add"/>
<s:property value="firstName"/> <s:property value="lastName"/>
</li>
I tried this...
$("#ul-available li").delegate("img", "click", function(){
$(this).click();
});
...thinking that if I click the image I could simply invoke the click event on the list item, but it doesn't work. Any ideas what I am doing wrong, or any other ideas on how to accomplish this would be much appreciated.
Thanks to any/all replies!
Inside .delegate() this is referring to the <img> still, you need to crawl up to the <li>...using .closest() is a good method, like this:
$("#ul-available").delegate("li img", "click", function(){
$(this).closest("li").click();
});
.delegate() is also intended to capture clicks, so it's better up on the <ul> instead of on each <li>, I've made this adjustment above.

Finding the last focused element

I'm looking to determine which element had the last focus in a series of inputs, that are added dynamically by the user. This code can only get the inputs that are available on page load:
$('input.item').focus(function(){
$(this).siblings('ul').slideDown();
});
And this code sees all elements that have ever had focus:
$('input.item').live('focus', function(){
$(this).siblings('ul').slideDown();
});
The HTML structure is this:
<ul>
<li><input class="item" name="goals[]">
<ul>
<li>long list here</li>
<li>long list here</li>
<li>long list here</li>
</ul></li>
</ul>
Add another
On page load, a single input loads. Then with each add another, a new copy of the top unordered list's contents are made and appended, and the new input gets focus. When each gets focus, I'd like to show the list beneath it. But I don't seem to be able to "watch for the most recently focused element, which exists now or in the future."
To clarify: I'm not looking for the last occurrence of an element in the DOM tree. I'm looking to find the element that currently has focus, even if said element is not present upon original page load.
this image http://droplr.com/174l8H+
So in the above image, if I were to focus on the second element, the list of words should appear under the second element. My focus is currently on the last element, so the words are displayed there.
Do I have some sort of fundamental assumption wrong?
document.activeElement is what you want. It's part of HTML5 and supported by all modern browsers, including IE.
According to the documentation (see 'caveats'), .live() in jQuery 1.4.1 supports focus, mapping to focusin. I'd suggest creating an element in common scope to hold the last focused element. Perhaps like so:
var lastFocused;
$('input.item').live('focusin', function(){
lastFocused = $(this);
});
How to determine which html page element has focus?
Has your answer (using document.activeElement gets you there for many browsers, but to make the ones that don't support it work you'll want to add the Javascript from that question's answer).
In the end, it was an error in code elsewhere that was confusing the DOM about who had focus.
The line was this: $('#item-add').find('input.item').focus();
And it needed to be this: $('#item-add:last').find('input.item').focus();
Because the added item is always last in the list.
Much has been learned, and I've tried to start and upvote accordingly. Particularly of note to the question at large:
.live events are not cumulative. Only code cruft is.
Set a variable outside of your function and update it within your function so you can access it in other functions as well.
jsfiddle.net and jsbin.com are awesome.
Holy HTML5, document.activeElement is good to know.
Thanks so much, SO, for all your help on this issue.
You can use the :last selector to only handle the event on the last <input> in the document (last at the time that the event was fired)
$('input.item:last').live('focus', function(){
$(this).siblings('ul').slideDown();
});

Categories