How to call the setActive() function for different events - javascript

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"));

Related

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>

.child() returns 2nd level items

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

Is there a better way to map menu click events to it's corresponding code

Okay, basically when I click a menu option a corresponding html is loaded into the content block of the layout page after executing some other set of codes, Now I have mapped each menu options' click event to it's corresponding set of codes to execute as follows;
<li onclick="changeContentTo('home');">About Us</li>
<li onclick="changeContentTo('rules');">Rules</li>
The mapping is done using switch case
case "rules":
/* something here */
$c.load("actsAndRules.htm" ) ;
break;
case "home":
/* something else here*/
$c.load("content.htm" ) ;
break;
I was wondering is there any better/proper method to do this?
I don't want to bind click event to a selector say $('.myLink').click() for example and no HTML5 coding, event handling will be the same as above
Basically what I want is to strip that mapping as much as possible out of the code and place it in a seperate obj ( which maybe resides in a seperate JS file ) something that will imitate a table/reference for mapping i.e. if I were to change a mapping I will only have to update that make-believe table.
Define a dictionary?
var $map = {'rules': 'actsAndRules.html', 'home': 'content.html'};
Now, instead of switch you can directly
$c.load($map[$arg]);
Assuming $arg is your function argument name.
EDIT
To run code only if any of the switch cases were satisfied, you can do something like this:
var $flag = false;
// your switch
...
case default:
$flag = true;
break;
...
// after switch
if (!$flag) {
// common code.
}
Try it like,
HTML
<li onclick="changeContentTo('home','content.htm');">About Us</li>
<li onclick="changeContentTo('rules','actsAndRules.htm');">Rules</li>
<script>
function changeContentTo(type,page){
$elesToHide=something;// let $elesToHide and $c are pre defined
if(!$elesToHide.is(":visible"))
$elesToHide.slideDown()
$c.load(page) ;
}
</script>
Try
<li class="nav-item" data-location="content.htm">About Us</li>
<li class="nav-item" data-location="actsAndRules.htm">Rules</li>
Then
$(function(){
$('.nav-item').click(function(){
if(!$elesToHide.is(":visible")){
$elesToHide.slideDown();
}
$c.load($this.data('location') ) ;
})
})
if(!$elesToHide.is(":visible"))
$elesToHide.slideDown();
this part seems to always be the same.
Just put it before your switch part.
Then, it only boils down to distinguish the url. You could just pass that directly:
<li onclick="changeContentTo('content.htm');">About Us</li>
<li onclick="changeContentTo('actsAndRules.htm');">Rules</li>
$c.load(parameterOfFunction ) ;
<li clickable load-link="content.htm">About Us</li>
<li clickable load-link="actsAndRules.htm">Rules</li>
$(function(){
$('clickable').click(function(e){
e.preventDefault();
$c.load($this.data('location') ) ;
});
});
<li id="l1" class="menuItem" data-item="home.htm" ></li>
<li id="l2" class="menuItem" data-item="actsAndRules.htm" ></li>
<li id="l3" class="menuItem" data-item="content.htm" ></li>
$(document).ready(function(){
$('.menuItem').click(function(e){
$c.load($(this).attr('data-item'));
});
});
Edit:
The HTML5 "data-" attribute will not be a problem even if you are not using HTML5. In ASP.Net the attributes are passed as-is to the client. Just leave the value blank where you don't need processing.
If you don't want to bind click on li, then you can bind it on the parent using event-delegation:
<ul id="list">
<li.....
<li.....
</ul>
$(document).ready(function(){
$('#list').click(function(e){
var $elem = $(e.target);
var content = $elem.attr('data-item');
if ($.trim(content)) {
$c.load(content);
}
});
});
Binding click to a selector is preferable over specifying onclick inline with each element.
You can easily wrap that snippet into a function and store it separately in external js file to be called from here.

reorder list elements - jQuery? [duplicate]

This question already has answers here:
How may I sort a list alphabetically using jQuery?
(10 answers)
Closed 5 years ago.
Is it possible to reorder <li> elements with JavaScript or pure jQuery. So if I have a silly list like the following:
<ul>
<li>Foo</li>
<li>Bar</li>
<li>Cheese</li>
</ul>
How would I move the list elements around? Like put the list element with Cheese before the list element with Foo or move Foo to after Bar.
Is it possible? If so, how?
var ul = $("ul");
var li = ul.children("li");
li.detach().sort();
ul.append(li);
This is a simple example where <li> nodes are sorted by in some default order. I'm calling detach to avoid removing any data/events associated with the li nodes.
You can pass a function to sort, and use a custom comparator to do the sorting as well.
li.detach().sort(function(a, b) {
// use whatever comparison you want between DOM nodes a and b
});
If someone is looking to reorder elements by moving them up/down some list one step at a time...
//element to move
var $el = $(selector);
//move element down one step
if ($el.not(':last-child'))
$el.next().after($el);
//move element up one step
if ($el.not(':first-child'))
$el.prev().before($el);
//move element to top
$el.parent().prepend($el);
//move element to end
$el.parent().append($el);
One of my favorite things about jQuery is how easy it is to write tiny little add-ons so quickly.
Here, we've created a small add-on which takes an array of selectors, and uses it to order the children of the target elements.
// Create the add-on
$.fn.orderChildren = function(order) {
this.each(function() {
var el = $(this);
for(var i = order.length - 1; i >= 0; i--) {
el.prepend(el.children(order[i]));
}
});
return this;
};
// Call the add-on
$(".user").orderChildren([
".phone",
".email",
".website",
".name",
".address"
]);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<ul class="user">
<li class="name">Sandy</li>
<li class="phone">(234) 567-7890</li>
<li class="address">123 Hello World Street</li>
<li class="email">someone#email.com</li>
<li class="website">https://google.com</li>
</ul>
<ul class="user">
<li class="name">Jon</li>
<li class="phone">(574) 555-8777</li>
<li class="address">123 Foobar Street</li>
<li class="email">jon#email.com</li>
<li class="website">https://apple.com</li>
</ul>
<ul class="user">
<li class="name">Sarah</li>
<li class="phone">(432) 555-5477</li>
<li class="address">123 Javascript Street</li>
<li class="email">sarah#email.com</li>
<li class="website">https://microsoft.com</li>
</ul>
The function loops backwards through the array and uses .prepend so that any unselected elements are pushed to the end.
Here is a jQuery plugin to aid with this functionality: http://tinysort.sjeiti.com/
something like this?
​var li = $('ul li').map(function(){
return this;
})​.get();
$('ul').html(li.sort());
demo
I was somewhat lost you may be wanting something like this...
$('ul#list li:first').appendTo('ul#list'); // make the first to be last...
$('ul#list li:first').after('ul#list li:eq(1)'); // make first as 2nd...
$('ul#list li:contains(Foo)').appendTo('ul#list'); // make the li that has Foo to be last...
more of it here1 and here2
Have a look at jquery ui sortable
http://jqueryui.com/demos/sortable/

javascript prototype problem

So I have a rather basic javascript problem which I have been slamming my head into a wall over for awhile:
<div class='alist'>
<ul>
<li class='group_1'> An Item </li>
<li class='group_1'> An Item </li>
<li class='group_2'> An Item </li>
</ul>
</div>
<div class='alist'>
<ul>
<li class='group_1'> An Item </li>
<li class='group_1'> An Item </li>
<li class='group_2'> An Item </li>
</ul>
</div>
<script>
function toggle_item( num ){
$$( 'li.group_' + num ).invoke('toggle');
}
</script>
Basically, I need to create a sweeper that sets the div to display:none if all the li are display:none.
I think it would start like:
function sweep(){
$$('div.alist').each( function( s ) {
ar = s.down().children
});
}
Any suggestions for good tutorials would be welcome as well
Something like this might get you started. You'll need to iterate through the children and check if they're visible. If any of them aren't, set a flag and break from the loop. If the flag is false then you don't need to hide the div.
function sweep(){
$$('div.alist').each( function( s ) {
var shouldHide = true;
var children = s.down().childElements();
children.each(function(li) {
if(li.visible()) {
shouldHide = false;
throw $break;
}
});
if(shouldHide) {
s.hide();
}
});
}
You could use the select() method of Element to find all li descendants. And run a method Array.all for each li and check if all return true. Hide the div if all return true.
function sweep() {
// check each div
$$('div.alist').each(function(element) {
var listItems = element.select('li');
// are the list items of this div hidden?
var listItemsHidden = listItems.all(function(item) {
return item.visible();
});
// hide the div too if so
if(listIemsHidden) {
element.hide();
}
});
}
This code is untested.
This is jQuery solution (Prototype must be something similar):
$('div.alist').css('display', function () {
return $(this).find('li:visible').length < 1 ? 'none' : '';
});

Categories

Resources