how to repeat same Javascript code over multiple html elements - javascript

Note: Changed code so that images and texts are links.
Basically, I have 3 pictures all with the same class, different ID. I have a javascript code which I want to apply to all three pictures, except, the code needs to be SLIGHTLY different depending on the picture. Here is the html:
<div class=column1of4>
<img src="images/actual.jpg" id="first">
<div id="firsttext" class="spanlink"><p>lots of text</p></div>
</div>
<div class=column1of4>
<img src="images/fake.jpg" id="second">
<div id="moretext" class="spanlink"><p>more text</p></div>
</div>
<div class=column1of4>
<img src="images/real.jpg" id="eighth">
<div id="evenmoretext" class="spanlink"><p>even more text</p></div>
</div>
Here is the Javascript for the id="firsttext":
$('#firstextt').hide();
$('#first, #firsttext').hover(function(){
//in
$('#firsttext').show();
},function(){
//out
$('#firsttext').hide();
});
So when a user hovers over #first, #firsttext will appear. Then, I want it so that when a user hovers over #second, #moretext should appear, etc.
I've done programming in Python, I created a sudo code and basically it is this.
text = [#firsttext, #moretext, #evenmoretext]
picture = [#first, #second, #eighth]
for number in range.len(text) //over here, basically find out how many elements are in text
$('text[number]').hide();
$('text[number], picture[number]').hover(function(){
//in
$('text[number]').show();
},function(){
//out
$('text[number]').hide();
});
The syntax is probably way off, but that's just the sudo code. Can anyone help me make the actual Javascript code for it?

try this
$(".column1of4").hover(function(){
$(".spanlink").hide();
$(this).find(".spanlink").show();
});

Why not
$('.spanlink').hide();
$('.column1of4').hover(
function() {
// in
$(this).children('.spanlink').show();
},
function() {
// out
$(this).children('.spanlink').hide();
}
);
It doesn't even need the ids.

You can do it :
$('.column1of4').click(function(){
$(this); // the current object
$(this).children('img'); // img in the current object
});
or a loop :
$('.column1of4').each(function(){
...
});
Dont use Id as $('#id') for multiple events, use a .class or an [attribute] do this.

If you're using jQuery, this is quite easy to accomplish:
$('.column1of4 .spanlink').hide();
$('.column1of4 img').mouseenter(function(e){
e.stopPropagation();
$(this).parent().find('.spanlink').show();
});
$('.column1of4 img').mouseleave(function(e){
e.stopPropagation();
$(this).parent().find('.spanlink').hide();
});

Depending on your markup structure, you could use DOM traversing functions like .filter(), .find(), .next() to get to your selected node.
$(".column1of4").hover(function(){
$(".spanlink").hide();
$(this).find(".spanlink, img").show();
});

So, the way you would do this, given your html would look like:
$('.column1of4').on('mouseenter mouseleave', 'img, .spanlink', function(ev) {
$(ev.delegateTarget).find('.spanlink').toggle(ev.type === 'mouseenter');
}).find('.spanlink').hide();
But building on what you have:
var text = ['#firsttext', '#moretext', '#evenmoretext'];
var picture = ['#first', '#second', '#third'];
This is a traditional loop using a closure (it's better to define the function outside of the loop, but I'm going to leave it there for this):
// You could also do var length = text.length and replace the "3"
for ( var i = 0; i < 3; ++i ) {
// create a closure so that i isn't incremented when the event happens.
(function(i) {
$(text[i]).hide();
$([text[i], picture[i]].join(',')).hover(function() {
$(text[i]).show();
}, function() {
$(text[i]).hide();
});
})(i);
}
And the following is using $.each to iterate over the group.
$.each(text, function(i) {
$(text[i]).hide();
$([text[i], picture[i]].join(', ')).hover(function() {
$(text[i]).show();
}, function() {
$(text[i]).hide();
});
});
Here's a fiddle with all three versions. Just uncomment the one you want to test and give it a go.

I moved the image inside the div and used this code, a working example:
$('.column1of4').each(function(){
$('div', $(this)).each(function(){
$(this).hover(
function(){
//in
$('img', $(this)).show();
},
function(){
//out
$('img', $(this)).hide();
});
});
});
The general idea is 1) use a selector that isn't an ID so I can iterate over several elements without worrying if future elements will be added later 2) locate the div to hide/show based on location relational to $(this) (will only work if you repeat this structure in your markup) 3) move the image tag inside the div (if you don't, then the hover gets a little spazzy because the positioned is changed when the image is shown, therefore affecting whether the cursor is inside the div or not.
EDIT
Updated fiddle for additional requirements (see comments).

Related

Efficient jQuery/JS - avoiding copy-paste the same code again & again

I need to write an efficient code in order to keep it simple & code file small, also I want to use the knowledge I hopefully get from here in future codes.
UPDATE: Just to be clear - my example is fixed "buttons" on browser window side & if you click on one of them, it takes you to div with same ID (look at code below).
IMAGE:
MY CODE EXAMPLE:
//Smooth Scroll Menu Links
jQuery('.div1').on('click', function(e) {
e.preventDefault();
jQuery('html,body').animate({scrollTop:jQuery(this.hash).offset().top-100}, 800);
});
//I have to copy-paste it 1000 times & only change the ".div1" to something else
//Note that I need a solution with different class names, not "div1", "div2" etc but e.g "location", "potato", "car" etc.
How to make this code working without writing same lines for every single div?
There got to be a way to get class from item you click & then scroll to item with same name ID, right? Or any other way to keep codes shorter in that kind of situations - otherwise it's just copy-paste-huge-file fest.
You can give each button a class so you can catch them all, than go to the element you want to scroll to by looking at the attribute of the clicked button :)
HTML
<div class="buttons" data-scroll="div1">div1</div>
<div class="buttons" data-scroll="div2">div2</div>
<div class="buttons" data-scroll="div3">div3</div>
<div class="content" id="div1">some content</div>
<div class="content" id="div2">some content</div>
<div class="content" id="div3">some content</div>
JS
$('.buttons').on('click', function(e) {
e.preventDefault();
var scrollTarget = $(this).data("scroll");
scrollme(scrollTarget);
function scrollme(target) {
$('html,body').animate({scrollTop:$("#"+target).offset().top}, 800);
}
});
example: http://jsfiddle.net/7gd66kr1/1/
try this i hope will work with you :
jQuery('div').on('click', function(e) {
e.preventDefault();
jQuery('html,body').animate({scrollTop:jQuery(this.hash).offset().top-100}, 800);
});
note that : this function will aplay on all divs in page
I think there are more than an unique solution. If you don't want to modify your HTML, then you need a condition to determine if the clicked element is an "active" one:
jQuery('div').on('click', function(e) {
var id = $(this).attr('class');
var scrollto = $('#' + id);
if(scrollto.length > 0) {
e.preventDefault();
jQuery('html,body').animate({scrollTop:scrollto.offset().top-100}, 800);
}
});
If you "can" modify your HTML, it's wise to add some attribute to the "active" elements (the ones that are buttons), so you know they scroll the page, or put them inside a container. The attribute you add may be a class or any other valid attribute (I recommend "rel", but might be "data" as spotted by #Iliya Reyzis , who selects by class and scrolls to "data") and select them by it:
<div class="div1" rel="mybutton">div1</div>
jQuery("[rel='mybutton']").on('click', function(e) {
var id = $(this).attr('class');
var scrollto = $('#' + id);
e.preventDefault();
jQuery('html,body').animate({scrollTop:scrollto.offset().top-100}, 800);
});
Hope it helps!!

How do I apply jQuery's slideToggle() to $(this) and do the opposite to all other elements?

What I'd like to do is have all elements of class collapsible_list not displayed by default (with one exception... see below*), and then toggle their display when their parent <div class="tab_box"> is clicked. During the same click, I'd also like for every other element of class collapsible_list to be hidden so that only one of them is expanded at any given time.
*Furthermore, when the page initially loads I'd also like to check to see if an element of collapsible_list has a child a element whose class is activelink, and if there is one then I'd like that link's parent collapsible_list element to be the one that's expanded by default.
Here's some sample html code:
<style>
.collapsible_list {
display: none;
}
.collapsible_list.active {
display: block;
}
</style>
<div id="sidebar">
<div class="tab_box">
<div class="collapsible_tab">2014</div>
<div class="collapsible_list panel-2014">
1
2
3
</div>
</div>
<div class="tab_box">
<div class="collapsible_tab">2013</div>
<div class="collapsible_list panel-2013">
<a class="activelink" href="/2013/1">1</a>
2
3
</div>
</div>
</div>
And here's where I'm currently at with the javascript (although I've tried a bunch of different ways and none have worked like I'd like them to):
$(document).ready(function() {
// This looks redundant to me but I'm not sure how else to go about it.
$(".collapsible_list").children("a.activelink").parent(".collapsible_list:not(.active)").addClass("active");
$(".tab_box").click(function() {
$(this).children(".collapsible_list").toggleClass("active").slideToggle("slow", function() {
$(".collapsible_list.active:not(this)").each(function() {
$(this).slideToggle("slow");
});
});
});
});
I hope that's not too confusing, but if it is then feel free to let me know. Any help is much appreciated.
Since you have a dom element reference that needs to be excluded use .not() instead of the :not() selector
jQuery(function ($) {
// This looks redundant to me but I'm not sure how else to go about it.
$(".collapsible_list").children("a.activelink").parent(".collapsible_list:not(.active)").addClass("active").show();
$(".tab_box").click(function () {
var $target = $(this).children(".collapsible_list").toggleClass("active").stop(true).slideToggle("slow");
//slidup others
$(".collapsible_list.active").not($target).stop(true).slideUp("slow").removeClass('active');
});
});
Also, instead of using the slide callback do it directly in the callback so that both the animations can run simultaniously
Also remove the css rule .collapsible_list.active as the display is controlled by animations(slide)
Try This.
$('.collapsible_tab a').on('click', function(e){
e.preventDefault();
$('.collapsible_list').removeClass('active')
$(this).parent().next('.collapsible_list').toggleClass('active');
});
Fiddle Demo
I think your code would be less complicated if you simply remembered the previously opened list:
jQuery(function($) {
// remember current list and make it visible
var $current = $('.collapsible_list:has(.activelink)').show();
$(".tab_box").on('click', function() {
var $previous = $current;
// open new list
$current = $('.collapsible_list', this)
.slideToggle("slow", function() {
// and slide out the previous
$previous.slideToggle('slow');
});
});
});
Demo

Collapse HTML if no content found

I want to be able to remove HTML elements if they contain no content.
Let's say we have some markup and are targeting all 'collapse' classes:
<div class='collapse'>[CONTENT?]</div>
If there is some content then don't do anything.
But if there is no content - no string characters or whitespace - then remove the div element completely.
This is easy to implement in the simple cases but with nested content it's slightly more more tricky.
Here is a demo, if you try removing the [CONTENTX?] strings and then seeing what the HTML structure is you'll notice that it doesn't work completely.
If a div only has other divs with no content then that should be treated as no characters or whitespace.
If we remove all [CONTENTX?] strings then we should see no HTML structure.
What ways are there to handle this?
jsFiddle: http://jsfiddle.net/97udq/
HTML:
<div id='container'>
<div class='collapse'>
[CONTENT1?]
</div>
<div class='collapse'>
[CONTENT2?]
<div class='collapse'>
[CONTENT3?]
<div class='collapse'>[CONTENT4?]</div>
<div class='collapse'>[CONTENT5?]</div>
</div>
</div>
</div>
Javascript:
$(function(){
// function
collapse();
// Show HTML structure
alert($('#container').html());
});
function collapse(){
// Loop thru all collapse elements
$('.collapse').each(function(){
// Check for pure whitespace
if($(this).html().replace(/\s+/g, '').length==0){
// Nothing to see, so remove.
$(this).remove();
}
});
}
CSS:
.collapse{
height:20px;
border:1px solid red;
}
I think this does the job;
It just uses text() instead of html();
Here's the documentation.
This one adds the trim(), but I thik that's not what you want.
function collapse(){
$('.collapse').each(function(){
if($(this).text().length==0){
$(this).remove();
}
});
}
Here's another way of accomplishing what you want. It recurses down the DOM pruning nodes from the bottom up. Hope this helps.
function prune(root) {
$.each($(root).children(), function(){
prune($(this));
});
if($(root).html().replace(/\s+/g, '').length==0 && $(root).hasClass("collapse")){
$(root).detach();
}
}
Code integrated into your JSFiddle
You need to recreate the .each() loop, but reversed. Just like that :
function collapse(){
var el = $('.collapse');
for(var i = el.length - 1; i >= 0; i--){
if(el[i].innerHTML.replace(/\s+/g, '').length==0){
$(el[i]).remove();
}
}
}
It will remove the childrens first, then check for parent.
Here a fiddle : http://jsfiddle.net/97udq/5/
EDIT :
I missunderstood your question, here's the right solution :
function collapse(){
$('.collapse').each(function(){
var $this = $(this)
var clone = $this.clone();
clone.children().remove();
if(clone.html().replace(/\s+/g, '').length==0){
$this.children().appendTo($this.parent());
$this.remove()
}
})
}
Basicly, you clone the current div, remove its children and then check if there is some text. If there's none, you append his children to his parent
Fiddle : http://jsfiddle.net/97udq/9/

hide one div when another is showing in jQuery?

I am trying to hide a div when another one is visible.
I have div 1 and div 2.
If div 2 is showing then div 1 should hide and if div 2 is not showing then div 1 should be visible/unhide.
The function would need to be function/document ready upon page load.
I've tried this but I'm not having any luck, can someone please show me how I can do this.
<script>
window.onLoad(function () {
if ($('.div2').is(":visible")) {
$(".div1").fadeOut(fast);
} else if ($('.div2').is(":hidden")) {
$('.div1').fadeIn(fast);
}
});
</script>
Add a class of hidden to each div, then toggle between that class using jQuery. By the way, window.onload is not a function, it expects a string like window.onload = function() {}. Also, put fast in quotations. I don't know if that's required, but that's how jQuery says to do it.
<div class="div1"></div>
<div class="div2 hidden"></div>
.hidden { display: none }
$(document).ready(function() {
if($(".div1").hasClass("hidden")) {
$(".div2").fadeIn("fast");
}
else if($(".div2").hasClass("hidden")) {
$(".div1").fadeIn("fast");
}
});
You should pass a string to the .fadeIn() and .fadeOut() methods.
Instead of .fadeIn(fast) it'll be .fadeIn("fast"). Same for .fadeOut().
And in general since you're already using jQuery it's better to wrap your code like this:
$(function () {
// Code goes here
});
It looks like you're using jquery selectors (a javascript library). If you're going to use jquery make sure the library is loaded properly by including it in the document header (google makes this easy by hosting it for you <script src="//ajax.googleapis.com/ajax/libs/jquery/1.9.0/jquery.min.js"></script>)
With jQuery loaded you can do it like this
$(document).ready(function(){
if ($('.div1').is(":visible")) {
$('div2').hide();
}
else if ($('.div2').is(":visible")) {
$('div1').hide();
}
});
WORKING EXAMPLE: http://jsfiddle.net/HVDHC/ - just change display:none from div 2 to div 1 and click 'run' to see it alternate.
You can use setTimeout or setInterval to track if these divs exists
$(function() {
var interval = window.setInterval(function() {
if($('#div2').hasClass('showing')) {
$('#div1').fadeOut('fast');
}
if($('#div2').hasClass('hidden')) {
$('#div1').fadeIn('fast');
}
}, 100);
// when some time u don't want to track it
// window.clearInterval(interval)
})
for better performance
var div1 = $('#div1')
, div2 = $('#div2')
var interval ....
// same as pre code

Unable to get correct offset of floated div

<div id="content">
<div class="oddpost">
<div class="arrow"></div>
</div>
<div class="oddpost">
<div class="arrow"></div>
</div>
<div class="oddpost">
<div class="arrow"></div>
</div>
<div class="oddpost">
<div class="arrow"></div>
</div>
</div>
$(function() {
if (($(".oddpost").position().left + $(".oddpost").width()) >= $("#content").width()) {
$('.arrow').hide();
}
});
http://jsfiddle.net/Ek5Gy/52/
In the code, I have a div(.arrow) nested on another div(.oddpost). What I want to do is hide the .arrow of the left .oddpost only. The idea is .arrow hides when .oddpost is near the left side of #content.
I've tried using offset but it gives the same offset().left value on all the oddpost div so all the arrow div still hides, even the one on the right.
Can anybody tell me how to fix this?
Your main problem lies in your selector usage:
// selecting stuff
$(".oddpost")
// doing stuff on the selection
.position().left
But, you'll have to know how those helper methods work on the given set. position (and many others) will only work on the very first element in the set. Not on every element.
So what you have to do is, iterate over each element in the set and do the test for every element independently, like:
$(".oddpost").each(function () {
if ($(this).prev().length === 0 || $(this).offset().left < $(this).prev().offset().left) {
$('.arrow', this).hide();
}
});
// or
$(".oddpost").each(function () {
if ($(this).position().left === 0) {
$('.arrow', this).hide();
}
});
http://jsfiddle.net/Ek5Gy/53/
The position() call returns values for the first selected element (it doesn't work on all selected elements as many other jQuery calls). Check Javadoc.
This is working for me:
$(function(){
$(".oddpost").each (function (idx, el) {
if ($(el).position().left < $(el).width()) {
$('.arrow', el).hide();
}
});
});
The modified jsfiddle: http://jsfiddle.net/Ek5Gy/54/
In your code, $('.arrow').hide() will always act on all DOM elements that match that particular selector (.arrow). Also, your calculations are only performed once, for the first .oddpost element, since that's the behavior of the position() jQuery function.
I just did not follow exactly what you meant by "near the left of content". I've altered your code a bit to reflect all these changes and prepared a little jsFiddle. Check it here.
Your example works fine.
In your example the value of:
$(".oddpost").position().left + $(".oddpost").width() // Equals 100
This is less the total of #content which is 270px. Your asking if it's greater than or equal too. If you want to hide the arrow when it's near the left try this:
if ($(".oddpost").position().left === 0) {
$('.arrow').hide();
}
EDIT:
$(function(){
$(".oddpost").each(function(){
var position = $(this).position().left;
alert (position);
if(position === 0){
// Do what you need to here.
}
});
});
This is what your looking for...

Categories

Resources