.child() returns 2nd level items - javascript

Well i think .child() function is my problem... But im not sure about this.
I have this in html
<div class="tabs" data-name="1st level tabs">
<ul class="nav">
<li>1st level link</li>
<li>1st level link</li>
</ul>
<ul class="content">
<li>
1st TEXT
<div class="tabs" data-name="2st level tabs">
<ul class="nav">
<li>2nd level link</li>
<li>2nd level link</li>
</ul>
<ul class="content">
</ul>
</div>
</li>
<li>1st TEXT</li>
</ul>
</div>
One element with class "tabs" inside another... ok; in JS:
$(function($) {
var Tabs = function(element,options){
self = this;
self.$element = element;
self.testdrive = function (){
console.log(self.$element.attr("data-name"));
}
self.$element.children(".nav").children("li").children("a").click(function(e){
e.preventDefault();
//Returns EVER 2nd level
self.testdrive();
//Triggering directly a Tabs instance returns EVER 2nd level
$(this).closest(".tabs").data("test.tabs").testdrive();
});
}
//JQuery plugin
$.fn.tabs = function () {
return this.each(function () {
var $this = $(this);
var data = $this.data('test.tabs');
//Creating only one instance of Tabs
if(!data){
data = new Tabs($this);
$this.data('test.tabs',data);
}
});
}
//Adding tabs plugin to all ".tabs"
$(function() {
$('.tabs').each(function(){
$(this).tabs();
});
});
}( jQuery ));
When $(element).tabs() called, creates a instance of function Tabs inside a data attribute test.tabs. If test.tabs has been defined only uses an old instance to preserve it.
To test it, i created a function called testdrive, to print in console "data-name" attribute when .tabs>.nav>li>a has been clicked.
The code works but, in console i receive "2st level tabs" if i clicked on 1st level item.
Is a problem with child() function? Something wrong in my code?
Thanks for help.

The problem is the variable declaration for self, since you haven't used var to declare it is created in the global context. So
var self = this;
also you are setting the data using key test.tabs, but is reading it using key simple.tabs
$(this).closest(".tabs").data("test.tabs").testdrive();
Demo: Fiddle
Also I think you can use self.$element.find(" > .nav > li > a").click(function (e) {}); to register the click handler

Related

How to call the setActive() function for different events

I want to add active class using below function:
function setActive() {
aObj = document.getElementById('pagesidebar2').getElementsByTagName('a');
for(i=0;i<aObj.length;i++) {
if(document.location.href.indexOf(aObj[i].href)>=0) {
aObj[i].className='active';
}
}
}
window.onload = setActive;
This function is only applicable to a list within pagesidebar2 div.
<div id="pagesidebar2">
<ul>
<li>Home</li>
<li>About</li>
<li>Blog</li>
<li>Contact</li>
</ul>
</div>
Let's say I want to use the same function to add active class to:
<div id="pagesidebar3">
<ul>
<li>uae</li>
<li>ASIA</li>
<li>AFRICA</li>
<li>Europe</li>
</ul>
</div>
What changes should i make to the setActive() function?
One solution would be to use an array of things you want to add the class to, and iterate over it. For instance, to do sidebars 2 and 3:
const idsOfThingsToSetActive = ['pagesidebar2', 'pagesidebar3'];
function setActive() {
idsOfThingsToSetActive.forEach(id => {
aObj = document.getElementById(id).getElementsByTagName('a');
for(i=0;i<aObj.length;i++) {
if(document.location.href.indexOf(aObj[i].href)>=0) {
aObj[i].className='active';
}
}
});
}
Another would be to parameterize (ie. add an argument to) your function, then call your function once for each thing you want to set active; #Gbr22 gave a good example of that in their answer.
Which approach you take is really a human concern: do you want a function that focuses several things, or a function that focuses one thing, and you use it multiple times?
The rest of your code might impact this: for instance, if you need to focus one or multiple elements elsewhere, besides onload, then you probably want to do things the same in both places so that you can re-use the function.
You could have the navigation id, as an argument to your setActive function
function setActive(nav) {
aObj = document.getElementById(nav).getElementsByTagName('a');
for(i=0;i<aObj.length;i++) {
if(document.location.href.indexOf(aObj[i].href)>=0) {
aObj[i].className='active';
}
}
}
onload = ()=>{setActive('pagesidebar2'); setActive('pagesidebar3')}
You can use a class instead of an ID. This is a great option as you won't have to modify the javascript every time you need to loop through another set of links, just give the parent a class of "sidebar".
function setActive() {
var _url = "https://stacksnippets.net/home.html";//document.location.href
aObj = document.querySelectorAll('.sidebar a');
for(i=0;i<aObj.length;i++) {
if(_url.indexOf(aObj[i].href)>=0) {
aObj[i].className='active';
}
}
}
setActive();
.active{color:red;}
<div class="sidebar" id="pagesidebar2">
<ul>
<li>Home</li>
<li>About</li>
<li>Blog</li>
<li>Contact</li>
</ul>
</div>
<div class="sidebar" id="pagesidebar3">
<ul>
<li>uae</li>
<li>ASIA</li>
<li>AFRICA</li>
<li>Europe</li>
</ul>
</div>
try querySelectorAll
function setActive() {
let aObj = document.querySelectorAll('#pagesidebar2,#pagesidebar3 a');
console.log(aObj);
for(i=0;i<aObj.length;i++) {
if(document.location.href.indexOf(aObj[i].href)>=0) {
aObj[i].className='active';
}
}
}
window.onload = setActive;
.active{ color:'red'}
<div id="pagesidebar2">
<ul>
<li>Home</li>
<li>About</li>
<li>Blog</li>
<li>Contact</li>
</ul>
</div>
<div id="pagesidebar3">
<ul>
<li>uae</li>
<li>ASIA</li>
<li>AFRICA</li>
<li>Europe</li>
</ul>
</div>
Rather than hard-code the div id, you would pass a reference to the div that you need to work on as an argument to the function.
Also, take a look at this re-worked syntax. getElementsByTagName() is inefficient and even more so when used within a loop. You should avoid using it and instead use .querySelectorAll(). You can then directly loop over the resulting collection with .forEach(), which eliminates the need for loop indexes.
And, are you sure you want your indexOf check to compare against 0? indexOf returns -1 if an item can't be found. 0 is a valid answer that means the searched on element was present.
function setActive(div) {
// Find all the <a> elements within the passed div reference and loop over them
div.querySelectorAll("a").forEach(function(a){
if(location.href.indexOf(a.href)>=0) {
a.className='active';
}
}):
}
window.onload = setActive(document.getElementById("pagesidebar2"));
// Then any other time you want to call the function, just pass the appropriate div
setActive(document.getElementById("pagesidear3"));

Put Duplicates under Common Element JavaScript/Jquery

I have the following code.Now I am building the list using Jquery. How do I do this using Javascript/JQuery?
Html(raw)after completion should look like this
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div>
<ul id="listOne">
<li class="columnItem">John</li><!--will be removed and put under CommonLister-->
<li class="columnItem">James</li>
<li class="columnItem">Mary</li><!--will be removed and put under CommonLister-->
</ul>
<ul id="listTwo">
<li class="columnItem">John</li><!--will be removed and put under CommonLister-->
<li class="columnItem">Mark</li>
<li class="columnItem">Mary</li><!--will be removed and put under CommonLister-->
</ul>
<ul id="CommonLister">
<li class="columnItem">John</li>
<li class="columnItem">Mark</li>
</ul>
</div>
Jquery/JavaScrpit
function myFunctioner(){
$(() => {
let names = [];
let nameSet = new Set();
$("li.columnItemer").each((idx, ele) => {
nameSet.add($(ele).html())
});
var $common = $("<ul>").addClass("commmonLister");
nameSet.forEach((name) => {
if ($("li:contains(" + name + ")").length > 1) {
$("li:contains(" + name + ")").remove();
$("<li>").addClass("columnItemer").html(name).appendTo($common);
}
});
$common.appendTo($(".CommonLister"));
});
}
The above code only works if the list already exists on HTML not when dynamically creating the list. I will be building the list by Ajax query. really appreciate in if you guys can show me how to implement the above code dynamically as the list is built on click event.
Here is what I've got. I don't use the new Javascript notation (not really a fan of it), though I'm sure you could transcribe what I've written into ES if you want to keep it consistent in your project.
I took a very similar approach to you, however I did not dynamically create the element. If you know this element will exist on the page anyway, my personal philosophy is just let it exist there and be empty so that you don't have to create it on your own.
If these lists are being loaded dynamically (something I couldn't really test out while using codepen) then put this into a function called after the list elements have been created. Preferably you would simply go through the data when it is loaded and make the applicable DOM changes only once, but sometimes we do what we must
$(function() {
$('#run-code').on('click', function(e) {
e.preventDefault();
//What were you doing? nope.
var currentItems = {}; //Blank object
var $mergeColumn = $('#CommonLister'); //Common list reference
$('.columnItem').each(function(i, el) {
var $el = $(el); //Notation I use to differentiate between the regular HTML Element and jQuery element
if (!currentItems.hasOwnProperty($el.html())) {
//Has this name come up before? if not, create it.
currentItems[$el.html()] = []; //Make it equal to a brand spanking new array
}
currentItems[$el.html()].push(el);
//Add the item to the array
});
$.each(currentItems, function(name, data) {
//Loop through each name. We don't actually use the name variable because we don't care what someone's name is
if (data.length > 1) {
//Do we have more than 1 element in our array? time to move some stuff
$.each(data, function(i, el) {
var $el = $(el); //See note above
if (i == 0) {
//If this is the first element, let's just go ahead and move it to the merge column ul
$el.appendTo($mergeColumn);
} else {
$el.remove(); //Otherwise, we've already got this element so delete this one.
} //end if/else
}); //end $.each(data)
} //end if data.length >1
}); //end $.each(currentItems)
}); //end $.on()
}); //end $()
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<button id="run-code" class="btn btn-success">Click Me</button>
<h4>List 1</h4>
<ul id="listOne">
<li class="columnItem">John</li>
<!--will be removed and put under CommonLister-->
<li class="columnItem">James</li>
<li class="columnItem">Mary</li>
<!--will be removed and put under CommonLister-->
</ul>
<h4>List 2</h4>
<ul id="listTwo">
<li class="columnItem">John</li>
<!--will be removed and put under CommonLister-->
<li class="columnItem">Mark</li>
<li class="columnItem">Mary</li>
<!--will be removed and put under CommonLister-->
</ul>
<h4>Common List</h4>
<ul id="CommonLister">
</ul>

Animating new additions to a generated list with CSS

I have a <ul> of elements like this:
<ul id="task-list">
<li class="task"></li>
<li class="task"></li>
<li class="task"></li>
<li class="task"></li>
</ul>
Every time a .task is changed, added, or removed, I generate the entire <ul> again through a Handlebars template and display it with .html():
function addHTML(allTasks) {
var tasks = compileTemplate(allTasks);
return $('#task-list').html(tasks);
}
function compileTemplate(allTasks) {
var source = $('#task-template').html();
var template = Handlebars.compile(source);
return template({
tasks: allTasks
});
}
The list uses Slip.js for reordering, and new items are added at the top of the list. The height of each item could be anything.
Is it possible to have a CSS animation for new additions where the list "slides down" to pop in the new task, or will I need to change my logic to add each task to an existing list, instead of generating the whole <ul> each time?
You can solve this with some simple jquery functions, although AngularJS would look much cleaner. A little example for adding and removing elements with JSFiddle example.
HTML
<ul id="task-list">
<li class="task"><button class="remove">remove</button>Lorem</li>
<li class="task"><button class="remove">remove</button>Ipsum</li>
<li class="task"><button class="remove">remove</button>Dilor</li>
<li class="task"><button class="remove">remove</button>Test</li>
</ul>
<li class="task" id="hidden-task"><button class="remove">remove</button>Test</li>
<button class="add">add last Child</button>
jQuery
//.remove is class of your remove button, task is the list element
$('.remove', '.task').on('click', function(){
var li = $(this).parent();
li.slideToggle({
'complete':function(){
li.remove()
}
});
})
//use hidden element as dummy
var element = $('#hidden-task');
//cache tasklist
var taskList = $('#task-list');
$('.add').on('click', function(){
//create a deep clone
var clone = element.clone(1,1);
taskList.append(clone);
setTimeout(function(){
clone.slideToggle()
}, 200);
});
var nth = 2;
$('.c2th').on('click', function(){
var clone = element.clone(1,1);
$('li').eq(nth-1).after(clone);
setTimeout(function(){
clone.slideToggle()
}, 200);
});

Cant get the value of <li data-*> element. Tried Javascript as well as Jquery

The following is my dropdown list.
<ul id="navBar" data-value="">
<li data-city-value="blore">Bangalore
<ul>
<li data-city-value="delhi">Delhi</li>
<li data-city-value="che">Chennai</li>
<li data-city-value="jaipur">Jaipur</li>
<li data-city-value="hyd">Hyderabad</li>
<li data-city-value="mum">Mumbai</li>
<li data-city-value="pune">Pune</li>
</ul>
</li>
</ul>
And the following are my methods I tried to access the data-city-value attribute.
Method 1)
var cityName = document.getElementById('navBar');
var city = cityName.getAttribute('data-city-value');
alert(city);
It alerts "null"
Method 2)
var cityName = document.getElementById('navBar');
var city = cityName.dataset.cityValue;
alert(city);
It alerts "undefined".
Method 3)
$('#navBar li').click(function() {
$(this).parent().data('value', $(this).data('cityValue'));
});
alert($('#city').data('value'));
It alerts "undefined".
I checked the syntax to get data value here
It would be of great help if you can help me find where I am doing mistake.
Thanks. :)
IN your first two methods you target the top ul with id navBar. In the third method you do $(this).parent() which again takes you to the ul element.
That element does not have the data-city-value attribute.
The jquery method should be
$('#navBar').on('click','li', function(e) {
var city = $(this).data('city-value');
alert(city);
return false;
});
Demo at http://jsfiddle.net/gaby/Mb7KS/
As pointed out by Gaby, you need to reach the element firts. Try this:
$('#navBar:first-child')
This is how you can iterate through your data-city-value attributes:
$('li[data-city-value]').each(function(index,element){
alert($(element).data('city-value'));
});
You can also check my jsFiddle example.
For click events:
$(document).ready(function()
{
$('li').click(function() {
alert($(this).data('city-value'));
return false;
});
});
You should return false because the top li element has inner elements and it was triggering inner li element click event.
My second jsFiddle demo.

What is wrong with my jQuery code?

I want to toggle a div from left to right when someone clicks on the h1 which is floating on right every thing is working just fine but I can't toggle it. Once the div appears it never goes away.
<script>
$(document).ready( function (){
/*
THIS COMMENTED SECTION IS WORKING BUT WITHOUT TOGGLE
$('h1').click(function (e){
e.preventDefault();
$('div').animate({
'left': 0
}, 'slow');
});
*/
// This is not working!
$("h1").click(function(){
$("div").toggle(function() {
$("div").animate({'left': '-32%'});
}, function() {
$("div").animate({height: '0'});
});
});
});
</script>
And the HTML:
<h1>Click here!</h1>
<div style="left: -32%; display: block;">Put whatever content you want here!
<ul>
<li>CLICK ON THE LINK</li>
<li>CLICK ON THE LINK</li>
<li>CLICK ON THE LINK</li>
<li>CLICK ON THE LINK</li>
</ul>
</div>
The multiple function argument form of .toggle() has been deprecated in jQuery 1.8 and removed in 1.9 and it had a built in click handler. So, it didn't work the way you are trying to use it and it is no longer supported.
Here's a replacement for the multi-function version of .toggle():
jQuery.fn.cycleFunctions = function() {
var args = Array.prototype.slice.call(arguments);
// initialize index into function arguments
var indexTag = "_cycleIndex";
this.data(indexTag, 0);
this.on("click", function(e) {
var self = $(this);
var index = self.data(indexTag) % args.length;
self.data(indexTag, index + 1);
return args[index].call(this, e);
});
return this;
};
Note, this already does the .click() handler for you so you don't put this inside a click handler.
So, combined with your code:
<h1>Click here!</h1>
<div class="toggle" style="left: -32%; display: block;">Put whatever content you want here!
<ul>
<li>CLICK ON THE LINK</li>
<li>CLICK ON THE LINK</li>
<li>CLICK ON THE LINK</li>
<li>CLICK ON THE LINK</li>
</ul>
</div>
// This works with two operations that are opposites of each other
// which your two animations are not
$("h1").cycleFunctions(function(){
$(this).next(".toggle").slideUp();
}, function() {
$(this).next(".toggle").slideDown();
});
You would then have to fix your CSS to that the two actions are reversible. In order to move with the left style, the item has to have a non-default position style setting. And, once the second click happens and the item has a zero height, the item will never be visible again so this could operate two clicks and never be seen again.
Here's a working demo: http://jsfiddle.net/jfriend00/Xj65L/
Take a look at the jquery documentation: http://api.jquery.com/toggle/
.toggle( [duration ] [, complete ] )
The first parameter is is not a function.

Categories

Resources