I have to convert user entered unordered lists in a content management system into a bootstrap menu (navbar) using jquery.
80% there apart from one challenge that I can't figure out a nice solution for - ie one that uses selectors rather than string manipulation or regex. After all, we all know that we never parse html with regex :)
So, using the limited UI tools at the user's disposal, they generate a list, usually a two level nested list something like this
<ul>
<li>Blah1
<ul>
<li><a href='http://xxxx'>Blah1a</a></li>
<li><a href='http://yyyy'>Blah1b</a></li>
<li>Blah1c</li>
<li><a href='http://zzzz'>Blah1d</a></li>
</ul>
</li>
<li><a href='http://aaaa'>Blah2</a></li>
<li>Blah3
<ul>
<li><a href='http://xxxx'>Blah2a</a></li>
<li><a href='http://yyyy'>Blah2b</a></li>
</ul>
</li>
</ul>
And so on... The important thing is that some of their list items are links, some are just text.
I need to select each block of text contained in an <li> that is not already wrapped in an <a> and wrap it in an <a href="#"> so that the above would be transformed into:
<ul>
<li><a href='#'>Blah1</a>
<ul>
<li><a href='http://xxxx'>Blah1a</a></li>
<li><a href='http://yyyy'>Blah1b</a></li>
<li><a href='#'>Blah1c</a></li>
<li><a href='http://zzzz'>Blah1d</a></li>
</ul>
</li>
<li><a href='http://aaaa'>Blah2</a></li>
<li><a href='#'>Blah3</a>
<ul>
<li><a href='http://xxxx'>Blah2a</a></li>
<li><a href='http://yyyy'>Blah2b</a></li>
</ul>
</li>
</ul>
Shouldn't be that difficult I'm sure, but after an hour of playing I'm getting nowhere.
Try
$('li').contents().filter(function(){
return this.nodeType == 3 && $.trim($(this).text()).length > 0
}).wrap('<a href="#" />')
Demo: Fiddle
One line of code here:
$("li").not(":has(>a)").wrapInner('<a href="#" />');
jsFiddle
So, if you want to just get the text nodes than, yeah, use .contents(), which will get all nodes, including text nodes. Here are 2 methods you can use below for this:
$("li").not(":has(>a)").contents().filter(function(){
return this.nodeType == 3 && $.trim($(this).text()).length > 0
}).wrap('<a href="#" />');
jsFiddle
And here is another approach, just for the sake of something different:
$("li").not(":has(>a)").each(function(){
var $contents = $(this).contents();
if ($contents.length)
$contents.eq(0).wrap('<a href="#" />');
});
jsFiddle
Related
How could I add toggle class? When I click on anchor tag it should add class to only next sibling element ( .treeUlChild ). i tried a lot and try to find solution but couldn't. I am new and this is my first project in javascript.
here is my html code.
<div id="treeList" class="treeDiv">
<ul class="treeUl">
<li>
GUIDELINES
<ul class="treeUlChild treeLevel2">
<li> Guidlines 1</li>
<li> Guidlines 2</li>
<li> Guidlines 3</li>
<li> Guidlines 4</li>
</ul>
<!-- End Child Ul -->
</li>
<li>
AFTER-SALES
<ul id="test" class="treeUlChild treeLevel2">
<li>xyz</li>
<li>
def
<ul class="treeUlChild treeLevel3">
<li>
ASSETS
<ul class="treeLevel4">
<li>DIGITAL</li>
<li>OOH</li>
<li>POS</li>
<li>PRINT</li>
<li>SOCIAL GIF</li>
<li>SOCIAL VIDEOS</li>
</ul>
<!-- End Child Ul -->
</li>
</ul>
<!-- End Child Ul -->
</li>
</ul>
<!-- End Child Ul -->
</li>
</ul>
<!-- End treeUl -->
</div>
This is my javascript code.
document.querySelector('#treeList ul li a').addEventListener("click", function(){
document.querySelector('.treeUlChild').nextSibling.classList.toggle('done');
});
One issue is nextSibling returns a node object, it's better you use nextElementSibling which returns an element node. The other issue is querySelector will always return the first element with the specified selector, so the changes will always be reflected on the same element whichever link you clicked. You may rather use querySelectorAll which returns all the elements as a node list, and loop through each element and apply the changes. Another thing is, it's better to use event.target to get clicked element and rather than using a selector again.
document.querySelectorAll('#treeList ul li a').forEach(elem => elem.addEventListener("click", function(){
event.target.nextElementSibling.classList.toggle('done');
}));
There is very simple way using Bootstrap by the way.
But if you want to do that with pure Javascript, you're on the right way to it.
So first, transform your query selector into a object e.g:
var el = document.querySelector('#treeList ul li a');
forEach method, querying the single object clicked in the array of multiple objects:
el.forEach(yourFunctionName());
Add functions to your elements:
<li><a onclick="yourFunctionName()" href="#"> Guidlines 1</a></li>
<li><a onclick="yourFunctionName()" href="#"> Guidlines 2</a></li>
<li><a onclick="yourFunctionName()" href="#"> Guidlines 3</a></li>
<li><a onclick="yourFunctionName()" href="#"> Guidlines 4</a></li>
ps: you can simplify this.
Structure your function:
function myFunctionName(){
document.querySelector('.treeUlChild').nextSibling.classList.toggle('done');
}
I have been on a project to build a website to help students with revision.
I decided to make use of a cool navbar I found online in the website as I am not much of a professional myself. However, the navbar elements (the navbar is a unordered list which has been styled) don't show when i put any sort of background picture in the 'top' div. When it doesn't have a background picture (like in the fiddle) it is impossible to use the navbar properly.
This is the navbar I used:
http://cssmenumaker.com/menu/responsive-flat-menu#
I have fiddled about the position on the cssmenu div, tried putting it in the nav id and classes (both have styles set in the CSS).
Here is the fiddle
https://jsfiddle.net/jgs5uqxr/
It says I need to attach some code with the fiddle so here is the formating of my navbar
<div id='cssmenu'>
<ul>
<li><a href='#'>Home</a></li>
<li class='active'><a href='#'>Products</a>
<ul>
<li><a href='#'>Product 1</a>
<ul>
<li><a href='#'>Sub Product</a></li>
<li><a href='#'>Sub Product</a></li>
</ul>
</li>
<li><a href='#'>Product 2</a>
<ul>
<li><a href='#'>Sub Product</a></li>
<li><a href='#'>Sub Product</a></li>
</ul>
</li>
</ul>
</li>
<li><a href='#'>About</a></li>
<li><a href='#'>Contact</a></li>
</ul>
</div>
I would have expected my chnages to make a working multi-level navbar which I can work with so I can add links to the educational material for students.
Thanks in advance.
I am trying to create a simple title spacer for my html code so that I don't have to type " " a lot and so the code looks more eat. However I have created this:
$(function() {
var subSpace = document.getElementById("subSpace");
var titleName = document.getElementById("subSpace").value;
subSpace.innerHTML = " " + titleName;
return false;
});
but it only runs against this code once:
<li><a href="#" id="subSpace" value="Start Menu" ></a></li>
<li><a href="#" id="subSpace" value="Tiles" ></a></li>
<li><a href="#" id="subSpace" value="Action Center" ></a></li>
<li><a href="#" id="subSpace" value="Settings" ></a></li>
<li></li>
<li></li>
<li></li>
So it runs Start Menu and properly spaces it the way I want but doesn't run tiles, action center, etc. Is there a way to fix this? Am I missing something? I could be way off as I am still learning.
Thank you in advance!
You should use a class instead of an ID as IDs are supposed to be unique. Other than that, you could be using CSS to add padding to the left:
.subspace {
padding-left:10px;
}
<li>Start Menu</li>
<li>Tiles</li>
<li>Action Center</li>
<li>Settings</li>
<li>Customizations</li>
<li>Windows Search</li>
<li>Microsoft Edge</li>
That's because you have multiple elements with an id of subSpace. You should use an id only once. If you have multiple elements with the same id, the first element will only be matched.
Also, on a side-note, you should really use CSS to style your page (i.e adding spacers/margins/paddings).
You have duplicates ids. Use class if you need to select multiple elements. You can select them using JQuery
<a href="#" id="subSpace" value="Start Menu" class="subSpace">
In your js:
$('.subSpace').html(' ');
I see another answer here which suggests a better way to do this only using CSS. You should consider that too.
Is there are any custom implementation of ComboBox element (i.e. drop-down list) without tag? May be some div with CSS or JavaScript?
you can try my code, only use html, css and jquery :
create html :
<div class='dropdown'>
<b>My Dropdown</b>
<span>▼</span>
</div>
<ul class='option'>
<li><a href='#'>Dropdown 1</a></li>
<li><a href='#'>Dropdown 2</a></li>
<li><a href='#'>Dropdown 3</a></li>
</ul>
for complete code visit : http://jsfiddle.net/g6jrt8ey/
Happy Coding.
I'm looking for some help with modifying an existing function which controls the highlighted states of a multi level js accordion menu. I have had to use javascript due to certain css elements not working in safari browsers.
My problem is due to the multi level aspect as when a sub link is clicked, the parent link above it then deselects. I need the parent link to stay active when its sub links are clicked and only deselects when a link outside of that list is clicked upon.
I understand the theory of adding a conditional statement but simply don't know how to apply it correctly within the function...any help would be very much appreciated.
Here is the existing function which tells a link to be active or selected:
var Lst;
function CngClass(obj){
if (Lst) Lst.className='.topnav';
obj.className='selected';
Lst=obj;
}
and here is the menu code:
<ul class="topnav">
<li>Home</li>
<li><a onclick="CngClass(this);" href="#">Top Link 2</a>
<ul>
<li><a onclick="CngClass(this);" href="#">Cookies</a></li>
<li><a onclick="CngClass(this);" href="#">Events</a></li>
<li><a onclick="CngClass(this);" href="#">Forms</a></li>
<li><a onclick="CngClass(this);" href="#">Games</a></li>
<li><a onclick="CngClass(this);" href="#">Images</a></li>
<li><a onclick="CngClass(this);" href="#">Navigations</a>
<ul>
<li><a onclick="CngClass(this);" href="#">CSS</a></li>
<li><a onclick="CngClass(this);" href="#">JavaScript</a></li>
<li><a onclick="CngClass(this);" href="#">JQuery</a></li>
</ul>
</li>
<li><a onclick="CngClass(this);" href="#">Tabs</a></li>
</ul>
</li>
<li><a onclick="CngClass(this);" href="#">Tutorials</a>
<ul>
<li><a onclick="CngClass(this);" href="#">HTML</a></li>
<li><a onclick="CngClass(this);" href="#">CSS</a></li>
<li><a onclick="CngClass(this);" href="#">JavaScript</a></li>
<li><a onclick="CngClass(this);" href="#">Java</a>
<ul>
<li><a onclick="CngClass(this);" href="#">JSP</a></li>
<li><a onclick="CngClass(this);" href="#">JSF</a></li>
<li><a onclick="CngClass(this);" href="#">JPA</a></li>
<li><a onclick="CngClass(this);" href="#">Contact</a></li>
</ul>
</li>
<li><a onclick="CngClass(this);" href="#">Tabs</a></li>
</ul>
</li>
<li><a onclick="CngClass(this);" href="#">Contact</a></li>
<li><a onclick="CngClass(this);" href="#">Upload script</a></li>
Many thanks for any help or ideas...
Oh dear, may I ask why you're not simply using JQuery? It simplifies your task so much more.
I've got a live example with JQuery (took me about 3 mins) you can try. I assume this is what you're trying to accomplish?
You can use the same method in normal javascript as well, except it's more work and probably less efficient.
The general idea is:
remove active class from the current li
find the 3rd parent from the clicked link (li, ul, li) and then apply the active class to
that one instead.
Here's the JQuery
$(document).ready(function(){
$(".topnav li a").click(function(){
//get the ul element
var checkElement = $(this).next();
if(checkElement.is('ul')) {
//check if it's visible
if(!checkElement.is(':visible')) {
$(this).parent().addClass('opened');
checkElement.slideDown();
}else{
$(this).parent().removeClass('opened');
checkElement.slideUp();
}
}
//get the tree node
var parentElement = $(this).parent().parent().parent();
if(parentElement.is('li')){
$(".topnav li.active").removeClass('active');
parentElement.addClass('active');
}
});
});
Here's an example where it highlights the li element you clicked.