Replacing text with jQuery goes wrong with multiple links - javascript

I am using the following code in a list of links to translate their title according to the language the user is pointing to. If the user is pointing, for example, to the Chinese language icon, the "article-title" gets replaced with its Chinese version. On mouse off, the title goes back to English. This works just fine.
Where I am running into problems: sometimes I need to add another link after the "article-title", on the same line. Whenever I do this, the script panics and flicks the title between blank and default, because it is finding a link, but it's not telling it to change anything.
How do I add exceptions to this script? How do I make it check to see if the link has the class "foo", and if it does, please just ignore it?
$.each($("li"), function(i, elements) {
var links = elements.getElementsByTagName("a");
var article_title = elements.getElementsByClassName("article-title")[0];
$.each(links, function(j, link) {
var previous_title = article_title.innerHTML;
link.addEventListener("mouseover", function() {
$(article_title).fadeTo(200, 0.5, function(){
article_title.innerHTML = link.title;
$(article_title).fadeTo(200, 1, function(){});
});
});
link.addEventListener("mouseout", function() {
$(article_title).fadeTo(300, 0.5, function(){
article_title.innerHTML = previous_title;
$(article_title).fadeTo(300, 1, function(){});
});
});
});
});

jQuery has a built in function called hasClass(className). Simply use that to see if the a element has that particular class.
$.each($("li"), function(i, elements) {
var links = elements.getElementsByTagName("a");
var article_title = elements.getElementsByClassName("article-title")[0];
$.each(links, function(j, link) {
var previous_title = article_title.innerHTML;
if (!$(link).hasClass("foo")) {
link.addEventListener("mouseover", function() {
$(article_title).fadeTo(200, 0.5, function(){
article_title.innerHTML = link.title;
$(article_title).fadeTo(200, 1, function(){});
});
});
link.addEventListener("mouseout", function() {
$(article_title).fadeTo(300, 0.5, function(){
article_title.innerHTML = previous_title;
$(article_title).fadeTo(300, 1, function(){});
});
});
}
});
});
Simply replace the class "foo" in the above example with the actual class name of links you wish to ignore.

Related

Triggering fluffy.js

I'm trying to make a simple tumblr theme using the fluffy plugin (https://github.com/mzdr/fluffy.js) but I've ran into a problem. The plugin only executes once on page load. I'm trying to get it to work with the infinite scroll plugin (http://www.infinite-scroll.com/) and I need the fluffy plugin to trigger whenever new content loads.
I'm fairly new when it comes to JS so I appreciate any help I can get. Thanks.
Edit added code:
<script src="https://code.jquery.com/jquery-2.2.2.min.js"></script>
<script src="https://npmcdn.com/imagesloaded#4.1/imagesloaded.pkgd.min.js"></script>
<script src="http://static.tumblr.com/hpghjri/co2nfnr1j/infinitescroll.min.js"></script>
<script src="http://static.tumblr.com/nxwjyyg/XWUob8gtq/fluffy.min.js"></script>
<script>
$(function(){
app.initInfinite();
app.onImagesLoad();
}); //end document ready
var app = {
'initInfinite' : function() {
var $container = $('.page-index .posts');
$container.infinitescroll({
navSelector:".pagination",
nextSelector:".pagination-next",
itemSelector:".posts__container",
appendCallback:true,
loading:{
finishedMsg:" ",
img:"data:image/gif;base64,R0lGODlhAQABAHAAACH5BAUAAAAALAAAAAABAAEAAAICRAEAOw==",
msg:null,
msgText:" ",
selector:null,
finished:function() {
}
}
},
function(newElements) {
var $newElems = $(newElements).css({opacity:0});
var $newElemsIDs = $newElems.map(function() {
return this.id;
Tumblr.LikeButton.get_status_by_post_ids($newElemsIDs);
}).get();
$newElems.imagesLoaded(function() {
$newElems.animate({opacity: 1});
//what to do when new elems appended
// I need to trigger fluffy here
});
var $newElemsIDs = $newElems.map(function() {
return this.id;
}).get();
Tumblr.LikeButton.get_status_by_post_ids($newElemsIDs);
});
},
'onImagesLoad' : function() {
$('.content .posts').imagesLoaded()
.always( function( instance ) {
console.log('all images loaded');
$('.overlay').addClass('hide');
$('.overlay__preloader').attr('class', '');
})
.done( function( instance ) {
console.log('all images successfully loaded');
});
}
}
</script>
This is your lucky day! I just released v2.1.0 which fixes your problem. Now you can create Fluffy objects on the fly like that:
// Start automatic detection
Fluffy.detect();
// Or provide a DOM node for single creation
var myElement = document.querySelector('#what-ever-you-like');
Fluffy.create(myElement);
// Or use a selector to create one
Fluffy.create('[data-fluffy-container]');
Don't forget to check out the updated docs.

How to toggle svg properties using Raphaeljs and jQuery

I'm making a sidebar with sliding, accordion-like areas, so that when you click on a heading the associated content toggles visibility, as per the portion of code below. The initAccordion function also appends an svg "+" to the headings to indicate their action.
The final task is getting the svg held in the cross variable to rotate by 45 degrees on each click - which is where I'm having trouble.
Obviously, the two statements in the makeButtons function aren't accessible outside their containing function. I've tried to re-factor the code, but it ends up being a jumbled mess and I can't help thinking there must be a simple solution.
toggleContent : function(){
this.toggleClass('on').next().slideToggle(200);
cross.transform("r45");
},
makeButtons : function(el) {
var btn = Raphael(el,15,15);
var cross = btn.path(".....");
},
initAccoridon : function(){
$('#eventSideBar').find('h3').each(function(){
var btn = $('<div/>', {
class : 'sideBarBtn'
});
btn.appendTo(this);
var btnContainer = btn.get(0),
$this = $(this);
sidebar.makeButtons(btnContainer);
$this.on('click', function(){
sidebar.toggleContent.call($this);
});
});
}
I'm not sure if this is better suited to the code-review section of SO - apologies if it is; I can move it on request.
Fiddle here...
Edit: I've managed to get part of the way there, although with multiple elements, I can't get the corresponding svg to rotate; just the last one. Fiddle here, and updated code:
makeSvg : function(el) {
this.btn = Raphael(el,15,15);
this.cross = this.btn.path(".....");
return {
btn : this.btn,
cross : this.cross
};
},
toggleContent : function(){
if (this.hasClass('on')) {
sidebar.cross.transform("r0");
} else {
sidebar.cross.transform("r45");
};
this.toggleClass('on');
},
initAccordion : function(){
$('body').find('h3').each(function(){
var btn = $('<div/>', {
class : 'sideBarBtn'
});
btn.appendTo(this);
var btnContainer = btn.get(0),
$this = $(this);
sidebar.makeSvg(btnContainer);
$this.on('click', function(){
sidebar.toggleContent.call($this);
});
});
}
You will need to pass a parameter of the relevant element or capture it somehow. This example should work for multiple objects, you'll just need to add the reverse action.
jsfiddle
Relevant bit...
var sidebar = {
makeSvg : function(el) {
var btn = Raphael(el,15,15);
return btn.path(pathString).attr({fill:"#fff",stroke:"none"});
},
toggleContent : function( myCross ){
this.toggleClass('on');
myCross.transform("r45");
},
initAccordion : function(){
$('body').find('h3').each(function(){
var btn = $('<div/>', {
class : 'sideBarBtn'
});
btn.appendTo(this);
var btnContainer = btn.get(0),
$this = $(this);
var myCross = sidebar.makeSvg(btnContainer);
$this.on('click', function(){
sidebar.toggleContent.call($this, myCross);
});
});
}
};

Number, hover (etc) effect not work after Ajax Load More

I'm using Drupal 7 and get my content with View module on page. And my pager Views Load More module. And my thumbnail effect hover, shadow etc. Image hover using this code:
var hoverImg = '<div class="hoverimg"></div>';
$(".thumb").each(function(){
$(this).children("div").each(function(){
$(this).find("a").append(hoverImg);
});
});
$(".thumb div").hover(function() {
$(this).find(".hoverimg").animate({ opacity: 'toggle' });
});
$(".thumb").hover(function() {
$(this).find("div").each(function(){
$(this).find(".shadow").fadeOut(500);
});
});
And getting number on my current thumbnail. This code:
var c = '';
var d = '';
$('.view-content div.views-row').each(function(){
c = 0;
d = 0;
var i = 1;
d = $(this).find('.thumbimg').length;
$(this).find('.thumbimg').each(function(){
sayi=i++;
$(this).append('<div class="img_no">0'+sayi+'</div>');
});
});
Everything is OK. All effects on start page. But when click Load More button, my effects can't work another page.
How do i solve this problem? Thanks.
The reason why it stops working is due to the hover function (and your other scripts/functions) only works on existing elements. So if you add something with ajax, it wont apply to that unless you reload the script after the ajax load.
Another option is to use live() or on() (for the hover part. On is the new version of live, added in jQuery 1.7).
Live and on listens for any existing or future elements.
A live script would look something like this:
$(".yourElement").live({
mouseenter:
function () {
// Do something
},
mouseleave:
function () {
// Do something
},
mousemove:
function () {
// Do something
}
});

Unintuitive removeClass() problem

I'm using this flip plugin, see the code in this fiddle. The goal is to flip one box at a time, e.g. the second box clicked should revertFlip() the previous one. During the animation I don't want the other boxes to be clickable. I noted that the removeClass() is not working.
<div class='flippable'>I'm unflipped 1</div>
...
<div class='flippable'>I'm unflipped 9</div>
$('.flippable:not(.reverted)').live('click',function()
{
var $this = $(this);
var $prevFlip = $('.reverted');
var $allBoxes = $('.flippable');
$this.flip(
{
direction: 'lr',
color: '#82BD2E',
content: 'now flipped',
onBefore: function()
{
$prevFlip.revertFlip();
$prevFlip.removeClass('reverted');
},
onAnimation: function ()
{
$allBoxes.preventDefault();
},
onEnd: function()
{
$this.addClass('reverted');
}
})
return false;
});
I'll appreciate a lot your advise and suggestions.
Edit:
Error Console Output: $allBoxes.preventDefault is not a function
I believe this has something to do with revertFlip() calling onBefore and onEnd. This is causing some weirdness with addClass and removeClass. Check out my modified example: http://jsfiddle.net/andrewwhitaker/7cysr/.
You'll see if you open up FireBug that onBefore and onEnd are called multiple times, with I think is having the following effect (I haven't exactly worked out what's going on):
The call to onEnd for the normal "flip" sets reverted class for the desired element.
The call to onEnd for the "revert flip" action sets the same element as above again when onEnd is called.
Here's a workaround. I've taken out using onBegin and simplified onEnd since I'm not exactly sure what's going on with the revertFlip() call:
$(function() {
var handlerEnabled = true;
$('.flippable:not(.reverted)').live('click', function() {
if (handlerEnabled) {
var $this = $(this);
var $prevFlip = $('.reverted');
var $allBoxes = $('.flippable');
handlerEnabled = false;
$prevFlip.revertFlip();
$prevFlip.removeClass("reverted");
$this.addClass("reverted");
$this.flip({
direction: 'lr',
color: '#82BD2E',
content: 'now flipped',
onEnd: function() {
handlerEnabled = true;
}
});
}
return false;
});
})
I'm using a boolean flag to enable and disable the event listener. Try out this example: http://jsfiddle.net/andrewwhitaker/bX9pb/. It should work as you described in your OP (only flipping one over at a time).
Your original code ($allBoxes.preventDefault()) is invalid, because $allBoxes is a collection of elements. preventDefault is a function on the jQuery event object.
Can you try this script
var $prevFlip;
$('.flippable:not(.reverted)').live('click',function() {
var $this = $(this);
var $allBoxes = $('.flippable');
$this.flip( {
direction: 'lr',
color: '#82BD2E',
content: 'now flipped',
onBefore: function() {
if($prevFlip){
$prevFlip.revertFlip();
$prevFlip.removeClass('reverted');
}
},
onAnimation: function () {
//$allBoxes.preventDefault();
//This is not a valid line
},
onEnd: function() {
$this.addClass('reverted');
$prevFlip = $this;
}
});
return false;
});
This reverts only one previous item. This is not a complete solution. I think there are more problems to this. I'll post any further updates if I found them.
I think #Andrew got the answer you are looking for.
You can filter $this out of $allBoxes by using the not() method.
$allBoxes.not($this).revertFlip();

Shorten the code for a lot of buttons in Prototype

I have a lot of buttons and by clicking on different button, different image and text would appear. I can achieve what I want, but the code is just so long and it seems very repetitive.
For example:
var aaClick = false;
$("aa").observe('click', function() {
unclick();
$('characterPic').writeAttribute('src',"aa.jpg");
$('characterBio').update("aatext");
$('aa').setStyle({ color: '#FFFFFF' });
aaClick = true;
});
$("aa").observe('mouseover', function() {
if (!aaClick) $('aa').setStyle({ color: '#FFFFFF' });
});
$("aa").observe('mouseout', function() {
if (!aaClick) $('aa').setStyle({ color: '#666666' });
});
function unclick() {
aaClick = false;
$('aa').setStyle({ color: '#666666' });
}
same thing with bb, cc, etc. and every time I add a new button, I need to add it to unclick function as well. This is pretty annoying and I tried to google it, and I only found observe click on all listed items, so I still couldn't figure out since what I want involves button up when other buttons are clicked.
Is there any way to just have a generic function that takes different id but do the exact same thing? Because from what I can see, if I can just replace aa with other id, I can reduce a lot of code. Thanks!!!
Build it all into a function where you can simply pass it the names of the DIVs you want to register. As long are you are consistent with your .jpg names, it should work.
var clicks = []
function regEvents(divName) {
$(divName).observe('click', function() {
unclick(divName);
$('characterPic').writeAttribute('src',divName+".jpg");
$('characterBio').update(divName"text");
$(divName).setStyle({ color: '#FFFFFF' });
clicks[divName] = true
});
$(divName).observe('mouseover', function() {
if (!clicks[divName]) $(divName).setStyle({ color: '#FFFFFF' });
});
$(divName).observe('mouseout', function() {
if (!clicks[divName]) $(divName).setStyle({ color: '#666666' });
});
}
function unclick(divName) {
clicks[divName] = false;
$(clicks[divName]).setStyle({ color: '#666666' });
}
Event delegation. Give all your buttons a class (eg yourButtonClass), then...
var clicks = [];
$$('.yourButtonClass').each(function(button) {
clicks.push(button.id);
});
$(document).observe('click', function(e) {
// If your click registered on an element contained by the button, this comes in handy...
var clicked = e.element().up('.yourButtonClass') || e.element();
if(clicked.hasClassName('.yourButtonClass')) {
unclick();
$('characterPic').writeAttribute('src', clicked.id + '.jpg');
$('characterBio').update(clicked.id + 'text');
$(clicked).setStyle({ color: '#FFFFFF' });
clicks[clicked.id] = true;
}
});
And so on...
The event handlers can have an argument that will be set to the event target. Then you could reuse the same function:
var clickedHandler = function(element) {
$(element).addClass("selected");
};
$("aa").observe('click', clickedHandler);
$("bb").observe('click', clickedHandler);
You're also in desperate need to CSS styling. Please see my answer to your previous question, but you could just reuse the pattern for the other events.
Do the buttons share a common container? Then the following code works:
$(container).childElements().each(function(element) {
$(element).observe('click', function () { … });
…
});
Alternatively, you can also do this:
["aa", "bb", "cc"].each(function(element) { … same code … });

Categories