Error when converting from jquery to mootools? - javascript

I have some code that uses jquery:
$(document).ready(function(){
$('#tabs div').hide();
$('#tabs div:first').show();
$('#tabs ul li:first').addClass('active');
$('#tabs ul li a').click(function(){
$('#tabs ul li').removeClass('active');
$(this).parent().addClass('active');
var currentTab = $(this).attr('href');
$('#tabs div').hide();
$(currentTab).show();
return false;
});
});
And I converted it to use mootools
window.addEvent('domready', function() {
$$('#tabs div').hide();
$$('#tabs div:first').show();
$$('#tabs ul li:first').addClass('active');
$$('#tabs ul li a').addEvent('click', function(event) {
$$('#tabs ul li').removeClass('active');
$$(this).parent().addClass('active');
var currentTab = $(this).attr('href');
$$('#tabs div').hide();
$$(currentTab).show();
return false;
});
});
But I get an error: $$(this).parent is not a function
How do I fix it?

this is quite poor. many bad practices and api differences.
window.addEvent('domready', function() {
// cache what we will reuse into vars
var tabs = document.id('tabs'),
divs = tabs.getElements('div'),
// for loop
len = divs.length,
ii = 1;
// hide all but the first one w/o extra lookups.
for (;ii < len;++ii)
divs[ii].hide();
// first match
tabs.getElement('ul li').addClass('active');
// attach the events to all links
tabs.getElements('ul li a').addEvent('click', function(event) {
event && event.stop();
tabs.getElement('ul li').removeClass('active');
this.getParent().addClass('active');
tabs.getElement(this.get('href')).show();
return false;
});
});
basically, a few practices you need to consider:
cache your selectors, esp repetitive stuff
avoid going to dom and work from memory
use normal js array looping or methods to avoid an extra selector like :first or :last, you already have the data
stop the event directly, don't return false
.getElement() will return the first match
avoid storing stuff into variables that you won't reuse
consider using event delegation and attaching a click handler once to the ul rather than to all child A's - eg, tabs.getElement('ul').addEvent('click:relay(li a)', fn) will achieve the same but only create a single event handler

there is no parent method in mootools, instead, the method name is getParent.it is not difficulty to convert from jquery to mootools. it is helpful having a look on docs.

Related

Doing jquery things angular way

I'm building a sidenavigation bar for my dashboard and in the process of building it, found a good tutorial which has built something similar. I'm planning to use this as a base work and will customise it afterwards. In the tutorial, the dynamic things have been handled using Jquery. Since I'm using Angularjs for my development, I want to do it Angular way. Can anyone please explain me how can I accomplish it.
Following is the link to tutorial.
Code snippets:
$("#menu-toggle").click(function(e) {
e.preventDefault();
$("#wrapper").toggleClass("toggled");
});
$("#menu-toggle-2").click(function(e) {
e.preventDefault();
$("#wrapper").toggleClass("toggled-2");
$('#menu ul').hide();
});
function initMenu() {
$('#menu ul').hide();
$('#menu ul').children('.current').parent().show();
//$('#menu ul:first').show();
$('#menu li a').click(
function() {
var checkElement = $(this).next();
if((checkElement.is('ul')) && (checkElement.is(':visible'))) {
return false;
}
if((checkElement.is('ul')) && (!checkElement.is(':visible'))) {
$('#menu ul:visible').slideUp('normal');
checkElement.slideDown('normal');
return false;
}
}
);
}
$(document).ready(function() {initMenu();});
How can I convert it to do line Angular way ?
Thanks
Replace this bind event with a View/Controller mapping
$("#menu-toggle").click(function(e) {
e.preventDefault();
$("#wrapper").toggleClass("toggled");
});
In the view
<div id="menu-toggle" ng-click="toggleClass"></div>
Then in your angular controller
$scope.toggleClass = function(){
angular.element('#wrapper').toggleClass('toggled')
};
You can do the same for this one
$("#menu-toggle-2").click(function(e) {
e.preventDefault();
$("#wrapper").toggleClass("toggled-2");
$('#menu ul').hide();
});
initMenu can be triggered in your main controller, just replace the $ with angular.element
If there are no old browser compatibility requirement, you should start using CSS3 class for transition instead of JS due to css perf vs js perf
function initMenu() {
angular.element('#menu ul').hide();
angular.element('#menu ul').children('.current').parent().show();
}
initMenu();
For the $('#menu li a').click() event, same, use the View/Controller mapping

Jquery selector - affecting element inside same DOM element

My CMS generates menus as lists without any id's and classes. For submenus, there are nested lists.
I made jquery script for expanding submenus:
$(function () {
$(".wrapper ul li").click(function () {
if ($(this).has("ul").length) {
$("a", this).removeAttr('href');
$("ul", this).slideToggle();
}
});
});
My problem is that this script reacts to clicking whole li area and I want it to react to clicking link inside li. Of course I just have to add "a" to selector making it ".wrapper ul li a" but what about condition checking if there is ul nested inside li? And slidetoggle selector. How should I change these?
This might work with minimal modification to your original code.
$(function () {
$(".wrapper ul li a").click(function ()
{
this = $(this).parent();
if ($(this).has("ul").length) {
$("a", this).removeAttr('href');
$("ul", this).slideToggle();
}
}
);
});
Test and see if it works. I have not tested it. Cheers

Mouseleave triggering when leaving grandparent div

I'm having a bit of trouble with a dropdown menu that triggers fadeOut as soon as the mouse leaves the grandparent div, I've searched this problem to death and have yet to find an elegant solution. Here is my code : link
var main = function() {
$('nav').mouseenter(function() {
$('ul li ul').fadeIn('400');
});
$('nav ul li').mouseleave(function(){
$('ul li ul').fadeOut('400');
});
}
$(document).ready(main);
DEMO: MY FIDDLE
You need to specify what element(s) you are trying to attach the event to. By adding '>' youre forcing to only attach the event to that element's children. Try this:
var main = function() {
$('nav').mouseenter(function() {
$(this).find('ul').fadeIn('400');
});
$('nav>ul>li').mouseleave(function() {
$(this).find('ul').fadeOut('400');
});
};
FIDDLE
$(this).find('ul').fadeOut('400');
is correct as $('ul>li>ul').fadeOut('400'); Could not target specific (current) li.
Use following hierarchical flow of TAGS
var main = function() {
$('nav').mouseenter(function() {
$('ul li ul').fadeIn('400');
});
$('nav ul li').mouseleave(function() {
$(this).find('ul').fadeOut('400');
});
};

JavaScript tabs works only when an individual tab is present

I have created a simple tabs widget, here is a working jsfiddle: http://jsfiddle.net/G3eRn/1/
When I add another Tabs widget "as you will see in the example" in the same page, everything get missed up! I really don't know how to describe it... after looking into it and trying to debug it, I have concluded that it is the JavaScript that needs to be looked at, so I have researched, but didn't really find an answer that would fix it. However, I found that using .each might fix it? so I did try it but didn't work - maybe I used it wrong.
What I am doing wrong?
Here is the JS:
//Tabs Navigation
$('.tabs .tabs-menu a:eq(0)').addClass('active');
$('.tabs .tabs-sections .tabs-section:not(:first-child)').hide();
$('.tabs .tabs-menu a').on('click', function() {
$('.tabs-menu a').removeClass('active');
$(this).addClass('active');
$('.tabs .tabs-sections > .tabs-section:eq(' + $(this).index() + ')').show().siblings().hide();
});
You have to make sure only the current tab group is being affected (using .each):
http://jsfiddle.net/G3eRn/2/
//Tabs Navigation
$('.tabs').each(function(){
var $tabs = $(this);
$tabs.find('.tabs-menu a:eq(0)').addClass('active');
$tabs.find('.tabs-sections .tabs-section:not(:first-child)').hide();
$tabs.find('.tabs-menu a').on('click', function () {
$tabs.find('.tabs-menu a').removeClass('active');
$(this).addClass('active');
$tabs.find('.tabs-sections > .tabs-section:eq(' + $(this).index() + ')').show().siblings().hide();
});
});
And here's a version that's a little more performant:
http://jsfiddle.net/G3eRn/7/
//Tabs Navigation
$('.tabs').each(function(){
var $tabs = $(this);
$tabs.find('.tabs-sections .tabs-section:not(:first-child)').hide().end()
.find('.tabs-menu a').on('click', function () {
$tabs.find('.tabs-menu a').removeClass('active').end()
.find('.tabs-sections > .tabs-section:eq(' + $(this).index() + ')').show().siblings().hide();
$(this).addClass('active');
}).eq(0).addClass('active');
});
The second example uses end() which "undoes" the last selector.
So for example
$('.el').find('div').end().addClass('test')
would add the class "test" to the .el element instead of all div's inside it.
In your "click" handler, you have to make sure that you're applying changes only to the relevant group of tabs. As your code is currently written, the handler code will affect all the matching tab groups on the page.
You can use jQuery DOM navigation methods to do it:
$('.tabs .tabs-menu a:eq(0)').addClass('active');
$('.tabs .tabs-sections .tabs-section:not(:first-child)').hide();
$('.tabs .tabs-menu a').on('click', function () {
// find the current menu group and deactivate all tab labels
$(this).closest('.tabs-menu').find('a').removeClass('active');
// activate this tab
$(this).addClass('active');
// find the tab section corresponding to this tab menu
$(this).closest('.tabs').find('.tabs-sections > .tabs-section:eq(' + $(this).index() + ')').show().siblings().hide();
});
The .closest() method walks up the DOM looking for a match. From that point, .find() looks down the DOM in that isolated subtree.
Here's your fiddle, updated.
Personally I'd use a delegated handler setup so that tab groups could be added dynamically without needing to re-run the code:
$('body').on('click', '.tabs-menu a', function() {
// as above
});
You need to apply the functions to every .tabs element.
$('.tabs').each(function() {
var $tabs = $(this);
$('.tabs-menu a:eq(0)', $tabs).addClass('active');
$('.tabs-sections .tabs-section:not(:first-child)', $tabs).hide();
$('.tabs-menu a', $tabs).on('click', function() {
$('.tabs-menu a', $tabs).removeClass('active');
$(this).addClass('active');
$('.tabs-sections > .tabs-section:eq(' + $(this).index() + ')', $tabs).show().siblings().hide();
});
});
The syntax jQuery( 'selector', node ) selects child elements only inside the given HTML element node. In this case an element with the class .tabs. This is similar to do jQuery( node ).find( 'selector' ).
Using $ is just a way for me to always know, which variable is a jQuery object. For example: var $this = jQuery( this );.
If you want more performance in your script store selected node in a variable (e.g. .tabs-menu a in your code). You may even querySelector to get the elements via plain JS instead of jQuery. The jQuery each on the other hand is very comfortable.
An example mixup of different methods:
$('.tabs').each(function() {
var tabs = this,
links = this.querySelectorAll( '.tabs-menu a' );
/* passing a DOMNodeList to jQuery and filter it */
$(links).filter(':eq(0)', tabs).addClass('active');
$('.tabs-sections .tabs-section:not(:first-child)', tabs).hide();
/* setup event handler */
/* without jQuery, one need to itererate over the list of elements */
for( var i = links.length; i--; ) {
var currentLink = links[i];
currentLink.addEventListener('click', function() {
var currentTab = this;
/* jQuery does the loop internally */
$(links).removeClass('active');
currentTab.classList.add('active');
$('.tabs-sections > .tabs-section:eq(' + $(currentTab).index() + ')', tabs).show().siblings().hide();
}, false);
}
});
For documentation and browser support see here:
https://developer.mozilla.org/en-US/docs/Web/API/EventTarget.addEventListener
https://developer.mozilla.org/en-US/docs/Web/API/Document.querySelector
https://developer.mozilla.org/en-US/docs/Web/API/Document.querySelectorAll
https://developer.mozilla.org/en-US/docs/Web/API/Element.classList
There is a JS ployfill for nearly any modern feature:
https://github.com/Modernizr/Modernizr/wiki/HTML5-Cross-browser-Polyfills

How do I simplify the function below?

I was able to achieve a content switcher with the block of code below but I'm looking for a way to simplify it. There are up to 10 or more topics to switch between, how do I simplify it so that the code wouldn't be too large, instead of having a block of code per DIV.
jQuery(document) .ready(function () {
$('.topic-intro:not(:nth-of-type(1))') .hide();
$('#mid-nav-in ul li:nth-of-type(1)') .addClass('active');
$('#mid-nav-in ul li a:nth-of-type(1)') .click(function () {
$('.topic-intro:not(:nth-of-type(1))') .hide();
$('.topic-intro:nth-of-type(1)') .show();
$('#mid-nav-in ul li:not(:nth-of-type(1))') .removeClass('active');
$('#mid-nav-in ul li:nth-of-type(1)') .addClass('active');
});
});
jQuery(document) .ready(function () {
$('#mid-nav-in ul li:nth-of-type(2) a') .click(function () {
$('.topic-intro:not(:nth-of-type(2))') .hide();
$('.topic-intro:nth-of-type(2)') .show();
$('#mid-nav-in ul li:nth-of-type(2)') .addClass('active');
$('#mid-nav-in ul li:not(:nth-of-type(2))') .removeClass('active');
});
});
It appears from your code that you are using the links in #mid-nav-in to show the corresponding .topic-intro and then hiding all others. It also appears that the code depends on the .topic-intro elements being in the same order as the links in the #mid-nav-in. If this is the case something like to following would work:
$('#mid-nav-in li a').on('click', function(){
// Remove 'active' Class from all <li/>
$('#mid-nav-in li').removeClass('active');
// Add 'active' Class to <li/> of Clicked Link
$(this).closest('li').addClass('active');
// Hide All .topic-intro elements
$('.topic-intro').hide();
// Show .topic-intro element at the Same index of Clicked Link
$('.topic-intro').eq($(this).closest('li').index()).show();
return false; // prevent default action
});
// Automatically Select the First Link
$('#mid-nav-in li a').eq(0).trigger('click');
Here is a fiddle as an example: http://jsfiddle.net/6hd97/2/
I hope this helps.

Categories