Dynamically build menu structure based on page elements [closed] - javascript

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 8 years ago.
Improve this question
i'm having some problems creating a dynamic menu based on the elements of the page. How would one create a menu from this?:
<div class="parent">
<div class="one child" id="first"></div>
</div>
<div class="parent">
<div class="one child" id="second"></div>
</div>
<div class="parent">
<div class="one child" id="third"></div>
</div>
<div class="parent">
<div class="one child" id="fourth"></div>
<div class="one child" id="fifth"></div>
<div class="one child" id="sixth"></div>
<div class="one child" id="seventh"></div>
</div>
<div class="parent">
<div class="one child" id="eight"></div>
</div>
So for this i would like jquery to build a menu structure like so:
<ul class="navigation">
<li></li>
<li></li>
<li></li>
<li>
<ul class="sub-navigation">
<li></li>
<li></li>
<li></li>
<li></li>
</ul>
</li>
<li></li>
</ul>
Here is the fiddle i've been meddling with (my attempt on making it work): http://jsfiddle.net/rt9Pm/
Somewhere along the way i lost my focus point and i'm unable to finish this little doodle.

I had some time, and thought this might still be of use to you:
(function($){
$.fn.createMenu = function (opts){
var s = $.extend({
'menuID' : 'menu',
'navigationID' : 'navigation',
'navigationClass' : 'navigation',
'attachTo' : 'body'
}, opts),
nav = $('<nav />', {
'id' : s.navigationID
}),
menu = $('<ul />', {
'id' : s.menuID
}),
textProp = 'textContent' in document.body ? 'textContent' : 'innerText',
ulWrap = document.createElement('ul'),
liWrap = document.createElement('li'),
aWrap = document.createElement('a'),
liTmp,aTmp,
// defining a function to create the li-wrapped links:
createLinks = function (el, par, prefix) {
// if the 'par' is a jQuery object we'll use that,
// otherwise we assume it's a DOM node and we wrap that with jQuery:
var parent = par instanceof jQuery ? par : $(par);
// cloning created elements rather than re-creating elements:
aTmp = aWrap.cloneNode();
// creating the 'href' to link to the id:
aTmp.href = '#' + el.id;
aTmp[textProp] = el.id;
liTmp = liWrap.cloneNode();
// appending the cloned a element to the li element:
liTmp.appendChild(aTmp);
// adding the appropriate class to the parent 'ul' element,
// and appending the 'li':
parent.addClass(('undefined' === typeof prefix ? '' : prefix) + s.navigationClass).append(liTmp);
};
// appending the 'menu' to the 'nav':
nav.append(menu);
// prepending the nav to the specified element (from the options/defaults):
nav.prependTo(s.attachTo);
// iterating over the elements matched by the selector:
this.each(function(i,e){
// using this twice, so caching:
var $e = $(e);
// if there are no siblings:
if ($e.siblings().length === 0) {
// we create the links:
createLinks(e, menu);
}
// if there are previous siblings we do nothing:
else if ($e.prev().length) {
// do nothing, this is inelegant
// but I couldn't think of a better way
}
else {
// there are siblings (this should only be matched by the first
// sibling of a group.
// clone a new 'li' and 'ul' element:
var li = liWrap.cloneNode(),
ul = ulWrap.cloneNode(),
// find all childNodes of the element's parent:
items = e.parentNode.childNodes;
// append the cloned 'ul' to the cloned 'li':
li.appendChild(ul);
// iterate over the childNodes:
for (var i = 0, len = items.length; i < len; i++) {
// if the node has a nodeType *and* that nodeType is exactly 1
// (therefore the node is an HTMLElement):
if (items[i].nodeType && items[i].nodeType === 1) {
// create links from those elements:
createLinks(items[i], ul, 'sub-');
}
}
// append the created 'li' (above, before the for loop) to the menu:
menu.append(li);
}
});
// I tend to return the created elements, jQuery often, however, returns
// the elements of the original selector (which would be 'return this'):
return nav;
};
})(jQuery);
$('.one').createMenu();
JS Fiddle demo.
References:
JavaScript:
Conditional (assessment ? assessment_true : assessment_false) 'ternary' operator.
document.createElement().
instanceof operator.
Node.appendChild().
Node.cloneNode().
Node.nodeType.
typeof operator.
jQuery:
addClass().
appendChild().
"How to Create a Basic jQuery Plugin."
jQuery.extend().
prependTo().
prev().
siblings().

var $parents = $('.parent'),
$ul = $('<ul>'),
$li = $('<li>'),
$a = $('<a>'),
$nav = $('<nav>'),
$navul = $ul.clone().attr('class', 'navigation');
$nav.append($navul);
$parents.each(function ea(){
var $parent = $(this),
$children = $parent.children('.one.child'),
$anchor = $a.clone(),
$subul,
id;
if ($children.length == 1) {
id = $children.attr('id');
$anchor
.attr('href', '#' + id)
.text($children.attr('id'));
$navul.append($li.clone().append($anchor));
} else if ($children.length > 1) {
$subul = $ul.clone().attr('class', 'sub-navigation');
$children.each(function ea(){
var $child = $(this),
$anchor = $a.clone(),
id = $child.attr('id');
$anchor
.attr('href', '#' + id)
.text($child.attr('id'));
$subul.append($li.clone().append($anchor));
});
$navul.append($subul);
}
});
$parents.filter(':eq(0)').before($nav);
http://jsfiddle.net/sPJPr/1

Related

Unable to remove previously assigned class with JavaScript

I have an ng-click function that filter list results. I am able to add a class with javascript to the clicked element but cannot remove it from the previously clicked elements. I was thinking I should iterate over the parentNode and then remove the class that I added. But, I am not getting the span elements in the childNodes list. How do I iterate over the other spans to remove the previously assigned class? Thank you.
console log of childNodes
NodeList(11) [text, comment, span.ng-binding.ng-scope, comment, span.ng-binding.ng-scope, comment, span.ng-binding.ng-scope, comment, span.ng-binding.ng-scope, comment, text]
// js function to assign class
$scope.updateTab = function(filterText,element) {
var parentElem = element.parentNode;
console.log(parentElem);
var children = parentElem.childNodes;
console.log(children);
children.forEach(child, function(child){
child.classList.remove('active-span');
})
element.classList.add("active-span");
if(!filterText){
$scope.searchText = filterText;
} else {
$scope.searchText = function(e) {
return (filterText.indexOf(e.AssignedTo) !== -1);
};
}
};
html
<div class="col-4" id="tabs">
<span ng-repeat="tab in tabs" ng-click="updateTab(tab.value,$event.currentTarget)">{{tab.label}}</span>
</div>

Finding the value of a class within a list

I have
<ul id="list">
<li data-markerid="0" class="">
<div class="list-label">A</div>
<div class="list-details">
<div class="list-content">
<div class="loc-id">2</div>
<div class="loc-addr">England</div>
<div class="loc-dist">2 miles</div>
<div class="loc-addr2">Test</div>
<div class="loc-addr2">Bristol</div>
</div>
</div>
</li>
<li data-markerid="1" class="">
<div class="list-label">A</div>
<div class="list-details">
<div class="list-content">
<div class="loc-id">3</div>
<div class="loc-addr">England</div>
<div class="loc-dist">60 miles</div>
<div class="loc-addr2">Test</div>
<div class="loc-addr2">London</div>
</div>
</div>
</li>
</ul>
I'm wanting to extract the value of this using JQuery.
I tried:
var targetID = $(this).find('.loc-id').text();
But this gets me values of both loc-id's. I want just the one that I'm selecting (clicking).
For full context, please look here: Parsing data using JQuery
$('#list').click(function () {
//Change the src of img
var targetID = $(this).find('.loc-id').text(); // Get the ID
// Since array of objects isn't indexed, need to loop to find the correct one
var foundObject = null;
for (var key in parsedArray) {
if (parsedArray.hasOwnProperty(key) && parsedArray[key].id == targetID) {
foundObject = parsedArray[key];
break;
}
}
// If the object is found, extract the image and set!
if (!foundObject)
return;
var imageSrc = foundObject.LocationPhoto; // From the object
$('#location-image').attr('src', imageSrc); // Set the new source
});
Thanks
In your click handler, this references the <ul> element which has multiple <li> children.
Change the click handler to act as a delegate instead:
$('#list').on('click', 'li', function () {
Now, inside the click handler, this references an <li> element so the search should only yield a single value.
For var targetID = $(this).find('.loc-id').text(); to work, you must be clicking an element that is an ascendant of only one .loc-id. For example:
$('.list-details').on('click',function(){
var targetID = $(this).find('.loc-id').text();
});
You need to change selector. In your event handler. $(this) referes to ul which has multiple loc-id thus when you are using text() its concatenating text.
Use
$('#list li').click(function () {
//Change the src of img
var targetID = $(this).find('.loc-id').text(); // Get the ID
alert('targetID: ' + targetID)
});
instead of
// When we select the item
$('#list').click(function () {
//Change the src of img
var targetID = $(this).find('.loc-id').text(); // Get the ID
DEMO
You could use event.target in case you are delegating on #list:
Demo: http://jsfiddle.net/abhitalks/CxcU8/
$("#list").on("click", function(e) {
var $t = $(e.target);
if ($t.hasClass('loc-id')) {
alert($t.text());
}
});

Create UL with data from a set of divs

I have a selection of divs with the exact markup below and I want to create an unordered list above those divs that will provide the ground work for a 'tab' system - all without modifying the below markup.
<div id="category_1">
<h3 class="maintitle">
<a class="toggle">..</a>
Cat 1 Title
</h3>
<div>
...
</div>
</div>
<div id="category_2">
<h3 class="maintitle">
<a class="toggle">..</a>
Cat 2 Title
</h3>
<div>
...
</div>
</div>
<div id="category_3">
<h3 class="maintitle">
<a class="toggle">..</a>
Cat 3 Title
</h3>
<div>
...
</div>
</div>
And I want to create with jQuery or just pure JS if easy enough:
<ul>
<li>Cat 1 Title</li>
<li>Cat 2 Title</li>
<li>Cat 3 Title</li>
</ul>
The rel would be the ID of the div so I know which tabs to show / hide later on.
The value of the LI item would be the text of the second anchor within the H3 of the original code.
Cheers.
Try this:
$(function () {
var $uls = $('<ul/>');
$('[id^=category]').each(function () { // Provide a container that hold the div as context.
var anch = $(this).find('h3 a').eq(1).clone(); //clone your anchor the second one in h3
anch[0].rel = this.id; // set the rel with the id
$('<li/>').append(anch).appendTo($uls); // append to temp ul
});
$('body').append($uls); // Append anywhere you want
});
http://jsfiddle.net/XmarF/
If you don't want to clone your anchor then you can try this too..
$(function () {
var $uls = $('<ul/>');
$('[id^=category]').each(function () {
$('<li/>').append($('<a/>', {
href: '#',
rel: this.id,
text: $(this).find('h3 a').eq(1).text()
})).appendTo($uls);
});
$('body').append($uls);
});
$('body').prepend($("<ul class='menu'>"));
$('div a[href]').each(function () {
var el = $(this).clone().attr('rel', $(this).closest('div').attr('id'))
el = $('<li></li>').append(el);
$('ul.menu').append(el);
});
Demo ---> http://jsfiddle.net/ht3Y7/
You could do something like this:
$('div[id^="category_"]').each(function(){
var id = $(this).attr('id');
var title = $(this).children('h3').find('a').eq(1).text();
var listItem = $("<li><a href='#'></a></li>");
listItem.find('a').attr('rel',id).text(title);
$('ul').append(listItem);
});
Fiddle
You could try using jQuery's .replaceWith() method. It will replace a current HTML with one you desire. So if your divs were in a wrapper with a name you could do something like:
$("#wrapperID > div").each(function(i) {
var li = $("<li />").html($(this).html());
$(this).replaceWith(li);
});
var ul = $("<ul />", { id: 'wrapperID ' }).html($("#wrapperID ").html());
$("#wrapperID ").replaceWith(ul);
|OR| if you're looking to convert to that exact markup, again I assume all divs in some type of wrapper with ID (for simplicity):
$("#wrapperID > div").each(function(i) {
var li = $("<li />"),
a = $("<a />", { href: "#", rel: $(this).prop("id"), text: $(this).find("a").last().text() }).appendTo(li);
$(this).replaceWith(li);
});
var ul = $("<ul />", { id: 'wrapperID ' }).html($("#wrapperID ").html());
$("#wrapperID ").replaceWith(ul);
See Working Example Here
With no jQuery, this will work in IE8 and up.
var divs = document.querySelectorAll("div[id^=category_]");
var ul = divs[0].parentNode.insertBefore(document.createElement("ul"), divs[0]);
for (var i = 0; i < divs.length; i++) {
ul.appendChild(document.createElement("li"))
.appendChild(divs[i].querySelector("a[href='#']").cloneNode(true))
.rel = divs[i].id
}
DEMO: http://jsfiddle.net/ENvNq/1/
You can do it in three lines (and maybe less):
var html='';
$('div[id^="category"]').each(function () {html += '<li>' + $(this).find('a.toggle').next().text() + '</li>';});
$('#category_1').before('<ul>'+html);
jsFiddle example

can this JavaScript function be refactored?

I need to use prototype JavaScript library on a project and wanted to add tabbed box to HTML.
The click handler has a simple task - set selected on parent <li> element and show linked DIV id element (rel tag on <li> has element id name)
<div class="tabInterface">
<ul class="tabSelector">
<li class="selected" rel="searchLast">Popularna iskanja</li>
<li rel="searchMine">Moje zadnje iskanje</li>
</ul>
<div class="tabContent selected" id="searchMine">
box 1 content
</div>
<div class="tabContent" id="searchLast">
box 2 content
</div>
</div>
Final result after 1 hour of hard labour.
initTabInterface = function() {
//requires prototype
var tabClick = function(event){
Event.stop(event);
var $el = Event.element(event);
var $menu = $el.up('.tabSelector');
var liList = $menu.descendants().filter(function(el){return el.match('li')});
liList.invoke('removeClassName', 'selected');
$el.up().addClassName('selected');
var rel = $el.up().readAttribute('rel');
var $interface = $menu.up('.tabInterface');
var tabList = $interface.descendants().filter(function(el){return el.match('.tabContent')});
tabList.invoke('removeClassName', 'selected');
$interface.down('#'+rel).addClassName('selected');
};
$$('.tabInterface .tabSelector li a').each(function(el){
var $el = $(el);
Event.observe($el, 'click', tabClick);
});
};
Event.observe(window,"load", function(){
initTabInterface();
});
Is there an easier way of traversing in prototype than with the bunch of up, down, filter, match, invoke and each?
This is pretty much you can get:
initTabInterface = function() {
//requires prototype
var tabClick = function(event){
Event.stop(event);
var $el = Event.element(event);
var rel = $el.up().readAttribute('rel');
// remove old selected classes
$el.up('.tabInterface').select('.selected')
.invoke('removeClassName', 'selected');
// add new selected classes
[ $(rel), $el.up() ].invoke('addClassName', 'selected');
};
$$('.tabSelector li a').each(function(el){
Event.observe($(el), 'click', tabClick);
});
};
Event.observe(window,"load", function(){
initTabInterface();
});​
I do not know Prototype well enough to quickly refactor your code, but you can use Element.siblings instead of going up and then descending down. Alternatively, you can just enumerate by class names (doesn't work well if you have more than one tab control).
$el.siblings().invoke('removeClassName', 'selected');
Also
$interface.down('#'+rel).addClassName('selected');
is unnecessary because you can only have one element with an id in the whole document. Can change it to:
$(rel).addClassName('selected');

Javascript show/hid div and change a css class based on which div is visible?

I have little piece of javascript to show/hide divs based on which link is clicked in a list. Its not very eligant, but it works fine. What I'd like to do is to assign an active state the a list item depending on which div is showing. Here is my JS and HTML:
var ids=new Array('section1','section2','section3','section4');
function switchid(id){
hideallids();
showdiv(id);
}
function hideallids(){
//loop through the array and hide each element by id
for (var i=0;i<ids.length;i++){
hidediv(ids[i]);
}
}
function hidediv(id) {
//safe function to hide an element with a specified id
document.getElementById(id).style.display = 'none';
}
function showdiv(id) {
//safe function to show an element with a specified id
document.getElementById(id).style.display = 'block';
}
html:
<ul>
<li class="activeItem">One</li>
<li>Two</li>
<li>Three</li>
<li>Four</li>
</ul>
<div id="section1" style="display:block;">1111111</div>
<div id="section2" style="display:none;">2222222</div>
<div id="section3" style="display:none;">3333333</div>
<div id="section4" style="display:none;">4444444</div>
When section 2 (or whichever) is clicked, I'd like the class "activeItem" to be removed from the li it is currently on and applied to the current li. Is this possible with javascript? I think it is, but I can't figure out how to implement it client side.
Thanks!
If you're able to use jQuery (or something similar) as it has this ability built in: http://docs.jquery.com/Attributes - addClass/removeClass
Change your anchors to use the onclick event instead of the href javascript code.
<a onclick="switchid('section1');return false;">One</a>
Then pass the argument this into your switchid function. this is a javascript keyword which in this scenario refers to the element (a).
<a onclick="switchid('section1', this);return false;">One</a>
Now to make the switchid function modify the list.
function switchid(id, el){
hideallids();
showdiv(id);
// rejiggered for text nodes
var li = el.parentNode.parentNode.childNodes[0];
while (li) {
if (!li.tagName || li.tagName.toLowerCase() != "li")
li = li.nextSibling; // skip the text node
if (li) {
li.className = "";
li = li.nextSibling;
}
}
el.parentNode.className = "activeItem";
}
Try it out: http://jsbin.com/esazu3/edit

Categories