Highlight current page link using Javascript - javascript

I'm having problem to highlight the active menu item in sidebar using Javascript. So I called setPage() in the to highlight current menu item, but nothing happens. Any ideas to solve it?
Btw, here's my code:
HTML:
<nav class="sidebar-nav">
<a class="sidebar-nav-item" href="nav.html">Main page </a>
<a class="sidebar-nav-item" href="page2.html">Dummy page 2</a>
<a class="sidebar-nav-item" href="page3.html">Dummy page 3</a>
<a class="sidebar-nav-item" href="page4.html">Dummy page 4</a>
<script language="text/javascript">setPage()</script>
</nav>
CSS:
* {
margin:0;
padding:0;
}
body {
background:#CCC;
font:140% "Times New Roman", Times, serif, Arial;
line-height:1.5;
font-weight:bold;
}
.sidebar-nav {
border-bottom: 1px solid rgba(255,255,255,.1);
}
.sidebar-nav-item {
display: block;
padding: .5rem 1rem;
border-top: 1px solid rgba(255,255,255,.1);
}
.sidebar-nav-item.active,
a.sidebar-nav-item:hover,
a.sidebar-nav-item:focus {
text-decoration: none;
background-color: rgba(255,255,255,.1);
border-color: transparent;
}
Javascript:
function extractPageName(hrefString) { // This function is
var arr = hrefString.split('/');
return (arr.length < 2) ? hrefString : arr[arr.length - 2].toLowerCase() + arr[arr.length - 1].toLowerCase();
}
function setActiveMenu(arr, crtPage) {
for (var i = 0; i < arr.length; i++) {
if (extractPageName(arr[i].href) == crtPage) {
arr[i].className = "sidebar-nav-item active";
}
}
}
function setPage() {
if (hrefString = document.location.href)
hrefString = document.location.href;
else
hrefString = document.location;
if (document.getElementsByClassName("sidebar-nav") != null)
setActiveMenu(document.getElementsByClassName("sidebar-nav-item"), extractPageName(hrefString));
}
Sorry for my bad English.

It's not language, but type, that's your first mistake (although it might work, it's not correct according to standards.
For setting link active state, get the according anchor (<a>) element and add class active to it. That should work as you've already defined style for active link, I think that should work as it is now.
Your have 2 problems in setPage function:
if (hrefString = document.location.href)
The = symbol means assignment of value, use == or === for comparison. I really recommend ===, because it literally compares 2 objects, instead only the value, as is the case with == comparison.
Second is that hrefString is not defined before this if statement (at least not in code you've provided), so javascript will throw error and stop execution.

Related

Vanilla JavaScript Tabs Using if-else Statements inside forEach Instead of a for Loop

I created tabs using Vanilla JavaScript but am struggling a little bit to understand why I can't replace a for loop with a forEach loop that is inside of a onTabSelectorClick function.
I tried replacing the for loop below:
for(i; i < tabSelector.length; i++) {
if(tabSelectorSelected.getAttribute('data-id') === tabContent[i].getAttribute('data-id')) {
tabContent[i].classList.add('tab-content-active');
} else {
tabSelector[i].classList.remove('active-tab-selector');
tabContent[i].classList.remove('tab-content-active');
}
}
With this forEach loop and it doesn't work:
tabSelector.forEach(function(singleTabSelector, i) {
if(singleTabSelector.getAttribute('data-id') === tabContent[i].getAttribute('data-id')) {
tabContent[i].classList.add('tab-content-active');
} else {
singleTabSelector.classList.remove('active-tab-selector');
tabContent[i].classList.remove('tab-content-active');
}
});
Is it not possible to use condition if-else statements inside forEach loops when interating over an array-like object?
var tabSelector = document.querySelectorAll('#tab-selectors > li');
var tabContent = document.querySelectorAll('#tab-contents > div');
tabSelector.forEach(function(singleTabSelector, i) {
singleTabSelector.setAttribute('data-id', i);
tabContent[i].setAttribute('data-id', i);
});
function onTabSelectorClick(e) {
var tabSelectorSelected = e.target;
if(!tabSelectorSelected.classList.contains('active-tab-selector')) {
var i = 0;
for(i; i < tabSelector.length; i++) {
if(tabSelectorSelected.getAttribute('data-id') === tabContent[i].getAttribute('data-id')) {
tabContent[i].classList.add('tab-content-active');
} else {
tabSelector[i].classList.remove('active-tab-selector');
tabContent[i].classList.remove('tab-content-active');
}
}
tabSelectorSelected.classList.add('active-tab-selector')
}
}
tabSelector.forEach(function(tabSelector) {
tabSelector.addEventListener('click', onTabSelectorClick);
});
.wrapper {
max-width: 960px;
margin: 0 auto;
}
#tab-selectors {
display: inline-block;
}
#tab-selectors > li {
padding: 10px;
}
#tab-selectors > .active-tab-selector {
border: 1px solid #f00;
}
#tab-content {
display: inline-block;
}
#tab-contents > div {
padding: 10px;
border: 2px solid #000;
height: 150px;
width: 150px;
display: none;
}
#tab-contents > .tab-content-active {
display: block;
}
<div class="wrapper">
<h1>Accessible Tabs using Vanilla JavaScript</h1>
<ul id="tab-selectors">
<li class="active-tab-selector">Tab Selector 1</li>
<li>Tab Selector 2</li>
<li>Tab Selector 3</li>
</ul>
<div class="break"><div>
<div id="tab-contents">
<div class="tab-content-active">
Tab Content 1
</div>
<div>
Tab Content 2
</div>
<div>
Tab Content 3
</div>
</div>
</div>
There's nothing special about how if statements works within a loop or a function. Your issue looks like a mere logic issue. Here's the before logic, in the for loop:
if(tabSelectorSelected.getAttribute('data-id') === tabContent[i].getAttribute('data-id')) {
tabContent[i].classList.add('tab-content-active');
} else {
tabSelector[i].classList.remove('active-tab-selector');
tabContent[i].classList.remove('tab-content-active');
}
...and here was your after logic using the .forEach:
if(singleTabSelector.getAttribute('data-id') === tabContent[i].getAttribute('data-id')) {
tabContent[i].classList.add('tab-content-active');
} else {
singleTabSelector.classList.remove('active-tab-selector');
tabContent[i].classList.remove('tab-content-active');
}
...which is equivalent to this (hopefully this makes comparing the two easier):
if(tabSelector[i].getAttribute('data-id') === tabContent[i].getAttribute('data-id')) {
tabContent[i].classList.add('tab-content-active');
} else {
tabSelector[i].classList.remove('active-tab-selector');
tabContent[i].classList.remove('tab-content-active');
}
Notice how between those two loops, in the second one you changed the element you're checking the data-id on from being the tabSelectorSelected to the singleTabSelector which is the tabSelector item you're looping over. The two pieces of code are not equivalent.
In the original code, you're comparing the ID of the selected tab with the ID of the tab content. In the second you're comparing the tab selector within the loop with the content element at the same index, so in all cases they'll have a matching data-id attribute.

My next and prev button for the list isn't working

The problem: I am not able to get the value of the HTML list from javascript. Each time user click next button, the program will count how many times the user clicks the next button. In javascript, I called the HTML list and iterate it. inside for loop, I called user click on next button number and add 1, The result of the sum will go inside the array and display that array line data. I don't know because of some reason I can't able to get data. Let me know if you get confused
Here is the HTML code
<div id="Border" class="">
<div id="Topic_List" class="creature">
<ul id="ListName" class="">
<li> Google </li>
<li>Facebook</li>
<li> Google </li>
</ul>
</div>
</div>
<div id="" class="" style="clear:both;"></div>
<div id="InputInsideFullCover" class="">
<textarea id="File_Name" name="File_Name"></textarea>
</div>
<button id="clickme" onclick="Next()"> Next </button>
<button onclick="Prev()"> Prev </button>
Here is the JavaScript code
<script>
function Next(){
var button = document.getElementById("clickme"),
count = 0;
button.onclick = function() {
count += 1;
var ul = document.getElementById("ListName");
var items = ul.getElementsByTagName("li");
for (var i = 0; i < items.length; ++i) {
var GetButtonClickValue = count;
var AddOne = GetButtonClickValue + 1;
var file_name = document.getElementById("ListName").innnerHTML=items[AddOne];
document.getElementById("File_Name").href = file_name;
var url_to_file = "http://www.example.com/"+file_name;
$.ajax({
url: url_to_file,
type:'HEAD',
error: function()
{
alert('data not found.');
},
success: function()
{
}
});
}
if(count > 9){
count = " ";
count = 1;
button.innerHTML = "Click me: " + count;
}
};
}
</script>
here is the CSS code
<style>
#Border{margin:5px auto;padding:0;width:50px;height:auto;border:1px solid #666;background-color:#f1f1f1}
#InputInsideFullCover{margin:5px;padding:0;width:700px;height:auto;}
#File_Name{margin:0;padding:0;width:638px;height:25px;resize:none;}
#Topic_List{margin:5px;padding:0;width:640px;height:auto;}
#Topic_List ul{margin:0px;padding:0;height:auto;width:12px;}
#Topic_List li{margin:0px;padding:0;list-style-type:none;float:left}
#button{
background-color: #4CAF50; /* Green background */
border: 1px solid green; /* Green border */
color: white; /* White text */
padding: 10px 24px; /* Some padding */
cursor: pointer; /* Pointer/hand icon */
width: 50%; /* Set a width if needed */
display: block; /* Make the buttons appear below each other */
}
.btn-group button:not(:last-child) {
border-bottom: none; /* Prevent double borders */
}
/* Add a background color on hover */
.btn-group button:hover {
background-color: #3e8e41;
}
</style>
You are declaring count to 0 at the beginning of the function. So every time the button is clicked count equals 0 and then one is added. (It will always be 1) You need to pull the count var outside of the function. This will also be useful for your Prev function. I bet that your Next and prev functions will be similar enough to make only one function. Call your function with true or false to add or subtract the count.
Just Add this
<script src="jquery-3.4.0.min.js"></script>
in your head tag before the external script tag
beacuse you are using ajax you need jquery

HTML - How do I insert a <span></span> tag into each line of a <pre></pre> block without hard coding?

I was just trying to add line numbers at the beginning of source code using CSS.
I realized the effect I wanted, as follows:
However, the HTML code required continual use of <span>...</span> tags:
<pre class="code">
<span>var links = document.getElementsByClassName("link");</span>
<span>for(var i = 0; i < links.length; i++){</span>
<span> links[i].onclick=function(){</span>
<span> alert(i+1);</span>
<span> };</span>
<span>}</span>
</pre>
With the span tags positioned at home/end of lines I can let the line numbers show as expected.
But I think there must be another better solution to prevent me adding all these span tags hard-coded, maybe using Javascript, or jQuery I don't mind but don't know how. Please help.
NOTE:
My problem is not how to display line numbers when the <span> tags are already there. Instead, I wanted to know if the origin HTML code contains NO <span> tags, how can I automatically add them into the suitable places and so I can apply the CSS styles.
This can be achieved by using CSS counters
This does not require any JavaScript (or jQuery) which means no need for each libraries or scripts and was introduced way back in CSS 2.1 so has great browser support across the board.
pre {
background: #eee;
counter-reset: section; /* Reset the counter to 0 for each new pre */
}
pre span:before {
counter-increment: section; /* Increment the section counter*/
content: counter(section); /* Display the counter */
padding: 0 5px;
border-right: 1px solid #777;
margin-right: 5px;
color: #777
}
<pre class="code">
<span>var links = document.getElementsByClassName("link");</span>
<span>for(var i = 0; i < links.length; i++){</span>
<span> links[i].onclick=function(){</span>
<span> alert(i+1);</span>
<span> };</span>
<span>}</span>
</pre>
<pre class="code">
<span>var links = document.getElementsByClassName("link");</span>
<span>for(var i = 0; i < links.length; i++){</span>
<span> links[i].onclick=function(){</span>
<span> alert(i+1);</span>
<span> };</span>
<span>}</span>
</pre>
I have combined #Stewartside answer with what you have actually asked for.
Below you can see a simple plain JavaScript to replace any line in element with code class to be wrapped in span which applies #Stewartside css.
var codeElement = document.getElementsByClassName("code"); //array of code blocks
var formattedCode = codeElement[0].textContent.replace("\r\n", "\n").split("\n");
var codeLength = formattedCode.length;
formattedCode.forEach(function(line, index, array) {
if (codeLength - 1 == index) return;
array[index] = "<span>" + line + "</span>";
});
codeElement[0].innerHTML = formattedCode.join("\n");
$(".code-jquery").each(function(index, codeElement) {
var formattedCode = $(codeElement).html().replace("\r\n", "\n").split("\n");
var codeLength = formattedCode.length;
$(codeElement).text("");
$.each(formattedCode, function(index, line) {
if (codeLength - 1 == index) return;
$(codeElement).append("<span>" + line + "</span>\n")
});
});
pre {
background: #eee;
counter-reset: section; /* Reset the counter to 0 for each new pre */
}
pre span:before {
counter-increment: section; /* Increment the section counter*/
content: counter(section); /* Display the counter */
padding: 0 5px;
border-right: 1px solid #777;
margin-right: 5px;
color: #777
}
pre.code-jquery span {
color: green;
}
<pre class="code">
var links = document.getElementsByClassName("link");
for(var i = 0; i < links.length; i++) {
links[i].onclick = function() {
alert(i+1);
};
}
</pre>
//jQuery version
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<pre class="code-jquery">
var links = document.getElementsByClassName("link");
for(var i = 0; i < links.length; i++) {
links[i].onclick = function() {
alert(i+1);
};
}
</pre>
So you basically need to append a span before each line. Here is the codepen link. I am currently using jQuery's insertBefore() method for it.
See below for the explanation of the code :-
$('.code') will give you the pre tag. Now the jQuery .find() method will give you all the spans inside the pre tag. Now jQuery .each() function is basically a for loop ( in simple terms ) which will loop for all the span tags inside the pre tag.
.insertBefore() function simply inserts whatever there is in the selector to the element mentioned inside the function.
var iCount = 1;
$('.code').find('span').each(function(){
$( "<span>"+iCount+"| </span>" ).insertBefore( this);
iCount++;
});
pre{
background: #eee
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<pre class="code">
<span>var links = document.getElementsByClassName("link");</span>
<span>for(var i = 0; i < links.length; i++){</span>
<span> links[i].onclick=function(){</span>
<span> alert(i+1);</span>
<span> };</span>
<span>}</span>
</pre>

Avoid event.target refer to the child instead of parent

I'm trying to get data attributes of a group of links and buttons, creating a event listener as follow:
// For all major browsers, except IE 8 and earlier
if (document.addEventListener) {
document.addEventListener("click", executeOnClick);
} else if (document.attachEvent) {
// For IE 8 and earlier versions
document.attachEvent("onclick", executeOnClick);
}
This event listener, executes the next function:
function executeOnClick(e){
//////////// Only elements which has "specialClass"
if (hasClass(e.target, 'specialClass')) {
if(e.target !== undefined){
console.info(e.target.getAttribute('data-info'));
e.preventDefault();
}
}
}
But doesn't work when the link or button has other tags inside them. Example:
<a data-info="Lorem ipsum 3!" href="#" class="specialClass">
<div>Link with div inside: <br> "event.target" is "div", not "a"</div>
</a>
I don't know how to get it work when the elements has and has no children. Somebody can help me?
Codepen of my problem: http://codepen.io/tomloprod/pen/gwaVXE
NOTE: I have omitted the definition of the hasClass method because this isn't the problem. Anyway, you can see it on the codepen.
You could use a function which will recursively check the parentNode for the presence of a data-info attribute.
Here is an example.
//////////// This function works well.
function findParentWithData(elem) {
try {
if(elem.getAttribute('data-info'))
return elem;
} catch(e) {
console.log('This was an anchor without data-info attribute.')
return e
}
while(!elem.getAttribute('data-info')) {
return findParentWithData(elem.parentNode);
}
}
function hasClass(event, className) {
if (event.classList) {
return event.classList.contains(className);
}
return new RegExp('(^| )' + className + '( |$)', 'gi').test(event.className);
}
function executeOnClick(e) {
// if click came from body don't do anything
if (e.target === document.body) return;
var result = document.getElementById("result");
result.innerHTML = "";
//////////// Only elements that has "specialClass"
// find parent with data-info
var elem = findParentWithData(e.target)
if (elem instanceof Element && hasClass(elem, 'specialClass')) {
if(elem !== undefined){
result.innerHTML = "Information: " + elem.getAttribute('data-info');
e.preventDefault();
}
}
}
// For all major browsers, except IE 8 and earlier
if (document.addEventListener) {
document.addEventListener("click", executeOnClick);
} else if (document.attachEvent) {
// For IE 8 and earlier versions
document.attachEvent("onclick", executeOnClick);
}
.btn {
opacity:0.8;
border:0;
-webkit-border-radius: 28;
-moz-border-radius: 28;
border-radius: 28px;
font-family: Arial;
color: #ffffff;
font-size: 37px;
padding: 10px 20px 10px 20px;
text-decoration: none;
outline:0;
margin: 0em 0 1em 0;
display: -webkit-inline-box;
}
.btn:hover {
cursor:pointer;
opacity:1;
text-decoration: none;
}
.btn.red{
background:#e74c3c;
}
.btn.green{
background:#2ecc71;
}
<div id="result"></div>
<a data-info="Lorem ipsum!" href="#" class="btn green specialClass">Link: Working well</a>
<button data-info="Lorem ipsum 2!" class="btn green specialClass">Button: Working well too</button>
<a data-info="Lorem ipsum 3!" href="#" class="btn red specialClass">
<div>Link with div inside: <br> Doesn't work</div>
</a>
<a data-info="Lorem ipsum 4!" href="#" class="btn red specialClass">
<ul>
<li>
Link with ul inside:
</li>
<li>
Doesn't work
</li>
</ul>
</a>
Foo

jQuery - prev/next navigation between non-siblings

I have a bunch of elements of the same type that have different parents, but I would like to be able to seamlessly navigate/cycle through all of them as if they were together.
<div>
<a href="#" class="open></a>
</div>
<div>
</div>
<div>
</div>
‹
›
I've managed to get this far: https://jsfiddle.net/pj0ecxge/
Currently it doesn't function as intended, as prev() and next() are only meant to target sibling elements, so the arrows don't work if the previous or next element is in another parent.
A single element will always be open by default, but it won't always be the same element as shown in the example. Also, only one element can be open at the same time.
If it makes a difference, I can add a single class to all children elements, but I can't change the HTML structure i.e put them all inside the same parent.
It would be nice if the navigation is infinite - i.e clicking next while the last element is open will show the first element and vice versa, but this is not required if it's too complex to do.
Thanks in advance and any help will be very appreciated!
You can check whether there are next/previous elements, if not then you can move a layer up/down like
$('.prev').click(function(e) {
e.preventDefault();
var current = $('.open');
var prev = current.prev();
if (!prev.length) {
prev = current.parent().prev('div').children('a:last-child')
}
if (prev.length) {
current.removeClass('open');
prev.addClass('open');
}
});
$('.next').click(function(e) {
e.preventDefault();
var current = $('.open');
var next = current.next();
if (!next.length) {
next = current.parent().next('div').children('a:first-child')
}
if (next.length) {
current.removeClass('open');
next.addClass('open');
}
});
* {
margin: 0;
padding: 0;
box-sizing: border-box;
text-align: center;
}
div {
font-size: 0;
}
div a {
width: 150px;
height: 150px;
border: 2px solid black;
}
a {
display: inline-block;
}
.open {
background: red;
}
.prev,
.next {
font-size: 100px;
text-decoration: none;
margin: 50px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div>
</div>
<div>
</div>
<div>
</div>
‹
›
Find the next set when the current set has reached either end. And the if the set is the last one then go back to the first one (and vice-versa).
$('.prev').click(function(e) {
e.preventDefault();
var current = $('.open');
var prev = current.prev();
if (!prev.length) {
prev = current.parent().prev('div').children('a:last-of-type');
if (!prev.length) {
prev = $('div:last-of-type').children('a:last-of-type');
}
}
current.removeClass('open');
prev.addClass('open');
});
$('.next').click(function(e) {
e.preventDefault();
var current = $('.open');
var next = current.next();
if (!next.length) {
next = current.parent().next('div').children('a:first-of-type');
if (!next.length) {
next = $('div:first-of-type').children('a:first-of-type');
}
}
current.removeClass('open');
next.addClass('open');
});

Categories