Toggle background image of a link for accordion - javascript

I'm trying to get a links background image to toggle or swap on click for an FAQ accordion expand/contractable div using javascript.
I've gotten things working based on this jsfiddle example (http://jsfiddle.net/QwELf/)
You can see my page working here (http://bit.ly/1hfgcGL)
The problem comes in when you click one of the toggle links a 3rd time. It gets the wrong background image and is then out of sync with how it should look.
Right arrow > for contracted state and downward arrow for expanded state are how they should be but the opposite shows up.
It seems to work just fine in the jsfiddle on the 3rd or more clicks any idea what's going wrong with mine?
Script
<script type="text/javascript">
function changeArrow(element){
var arrow = element;
if(arrow.className == 'background_one'){
arrow.className = 'background_two';
} else {
arrow.className = 'background_one';
}
}
</script>
CSS
.background_one { text-decoration: none; padding-left: 26px; background: url(http://skmgroupwork.com/suntrust/landingpages/307m/chevright.gif) center left no-repeat;}
.background_two { text-decoration: none; padding-left: 26px; background: url(http://skmgroupwork.com/suntrust/landingpages/307m/chevdown.gif) center left no-repeat;}
HTML
<a class="background_one" data-toggle="collapse" data-target="#demo4" onClick="changeArrow(this)">If I transfer my balance to a new Access 3 Equity Line, what will my interest rate be?</a>

You need to check if it has class, not if it is, as you have several on times. As you use jQuery you can use .hasClass(), .addClass() and removeCLass(). You might also want to look at .toggleClass().
function changeArrow(element) {
var $arrow = $(element);
if ($arrow.hasClass('background_one')) {
$arrow.removeClass('background_one');
$arrow.addClass('background_two');
} else {
$arrow.removeClass('background_two');
$arrow.addClass('background_one');
}
}

That is happening because the className also contains the class collapsed the second time it's clicked.
I used IE's debugger and found this:
Perhaps you could use contains instead of equals, like the following (untested, but should work):
function changeArrow(element){
element.className = (arrow.className.contains('background_one') ?
'background_two' :
'background_one');
}

Related

I'm having trouble understanding why a single click in my code is selecting text

I'm trying to make a little menu, that opens when you click on it, and closes when you click outside it. I managed to do that, mostly. The button I press to open the menu has some padding, and when I click specifically on the padding the menu that opens has all it's text selected, which is weird, and I don't understand why it happens. I have looked through MDN's documentation, my favorite search engine, and IRC(freenode) for a solution, but so far no luck.
I made a minimal working example, which I link to at the bottom, and I added a few comments to it about some lines you can comment to change the behavior. Simply, you can press the blue square, and the letter 'a' will show up selected, the expected behavior is that the letter 'a' should show up, but unselected. If you understand what is going on please let me know. ^_^
Edit: I see some discussion bellow about different results on different browsers. I'm currently using Firefox 59.0.1 64-bit on Linux(Fedora 27). A suggestion was made that this might be a bug, I can't rule that out.
https://jsfiddle.net/16k672tt/4/
function outside_function(event) {
var outside = event.target;
outside.classList.remove("outside");
var menu_list = document.getElementById("menu-list");
menu_list.classList.remove("menu-list-open");
event.stopPropagation();
}
var outside = document.getElementById("outside");
outside.addEventListener("click", outside_function);
function menu_click_event(event) {
var menu_list = document.getElementById("menu-list");
var outside = document.getElementById("outside");
menu_list.classList.add("menu-list-open");
outside.classList.add("outside");
event.stopPropagation();
}
var menu = document.getElementById("menu");
menu.addEventListener("click", menu_click_event);
.menu {
/* If you comment the next line, it renders the way I expect */
display: flex;
width: 2.5rem;
height: 2.5rem;
background-color: #0000ff80;
}
.outside {
position: fixed;
top: 0;
bottom: 0;
left: 0;
right: 0;
}
.menu-list {
display: none;
}
.menu-list-open {
display: block;
/* If you comment the next line, it renders the way I expect */
/* In fact if you decrease the alpha value to 0 it works as well (#ffffff00) */
background-color: #ffffffc0;
}
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
</head>
<body>
<div id="outside"></div>
<div id="menu" class="menu">
<div id="menu-list" class="menu-list">
a
</div>
</div>
</body>
</html>
I had a little talk with emilio on the #servo channel of mozilla's IRC network. He suggested the mouse down event is being triggered on the menu element, and the mouse up event is being triggered on the outside element. The browser assumes this was a drag motion, and selects the text in between. The problem seems to be the mouse up event is placed in the event queue after the click event. So even if the event propagation is stopped, there is already another event on the queue, mouse up, that will end up targeting the newly placed outside element. This could be a bug, so I'm off to https://bugzilla.mozilla.org/ to see if that is the case.
There have been work-arounds suggested, like using CSS's user-select to make the text unselectable, and the use of Javascript's preventDefault() to avoid running the default event handlers. Other stackoverflow users mentioned these but were unfortunately down-voted, and subsequently deleted their answers. I'm leaving these work-arounds here anyway, for anyone that might need them.
Thanks to all the people that helped look into this issue.

using CSS content & :after to show a <span> inside an image

I am working on an image gallery inside a web template, similar to this link. now currently when i click on an image it will be shown inside a jquery slider. but i need the image inside the slider to show the span defined inside the markup <span><strong>Project name:</strong><em>villa2</em></span>, where each image will have the following markup :-
<li>
<figure><img src="http://livedemo00.template-help.com/wt_47767/img/page3_img1.jpg" alt=""><span><strong>Project name:</strong><em>villa2</em></span></figure>
</li>
so i define the following inside my css file:-
#gallerySlider .placeholder:after {
content: "test";
color: yellow;
font-size: 36px;
font-family: arial;
position: relative;
display: block;
height: 20px;
width: 200px;
z-Index: 1;
bottom: 42px;
text-align: right;
margin: -8px;
}
which will show the following :-
where i was able to add the text "test " inside the slider. but can anyone adivce how i can do these 2 modifications:-
1) instead of showing a static text inside the slider using the following css:-
#gallerySlider .placeholder:after {
content: "test";
how i can force the CSS content , to show the <span><strong>Project name:</strong><em>villa2</em></span> which is related to the rendered image ?
2) how i can move the content to be below the image instead of being on the left ?
Thanks
EDIT
now after many tests and modifications i did the following modification to the loadImage callback function inside the Touchtouch script :-
function loadImage(src, callback){
var img = $('<img>').on('load', function(){
callback.call(img);
});
img.attr('src', src);
// get all the captions (should probably be done with variable referring to something about above selector)
var allcaptions = $("figure span");
// setTimeout is a hack here, since the ".placeholders" don't exist yet
//setTimeout(function () {
$(".placeholder").each(function (i) {
// in each .placeholder, copy its caption's mark-up into it (remove the img first)
var caption = allcaptions.eq(i).clone();
// caption.find("img").remove();
$(this).append("<div class='caption'>" + caption.html() + "</div>");
});
// }, 500
//);
}
this seems to show the span as follow:-
but i am facing two problems:-
First Problem as shown on the above picture the text will be shown twice , specifically when i click on the next and previous arrows, here is the built-in code for the next & prevoise arrows :-
function showNext(){
// If this is not the last image
if(index+1 < items.length){
index++;
offsetSlider(index);
preload(index+1);
}
else{
// Trigger the spring animation
slider.addClass('rightSpring');
setTimeout(function(){
slider.removeClass('rightSpring');
},500);
}
}
function showPrevious(){
// If this is not the first image
if(index>0){
index--;
offsetSlider(index);
preload(index-1);
}
else{
// Trigger the spring animation
slider.addClass('leftSpring');
setTimeout(function(){
slider.removeClass('leftSpring');
},500);
}
}
Second problem. is that as i click on next & previous arrows the image will keep loosing its position and after many next & previous i will end up having the image on the following position at the bottom of the page:-
so can anyone adivce how i can fix these 2 issues ?
Pseudoelements :after and :before make possible to add an element into the html flow with just css... but that's it. An element which can be text, shapes, images (using css instructions as content:url('image.png'), etc. But you can't add html in the content:of the pseudoelement (as logn as I know).
You could use simple jquery as:
$(".placeholder").after("<span class="your-class-to-style"><strong>Project name:</strong><em>villa2</em></span>");
But if you have control of the html I would just include the span there.
(note: :after and:before follow the html flow so to reposition them you need to use position:absolute not forgetting to add relative to the parent)
Well, if you check the sources, you will see they are using the TouchTouch jQuery plugin (a little bit outdated by the way). Since the plugin doesn't have options or callbacks, you will have to do this manually. The key is in the following line:
loadImage(items.eq(index).attr('href'), function(){
placeholders.eq(index).html(this);
});
So, even if you add the texts with jQuery, the plugin will remove it from the placeholder and add only the image.

moving glyph icon toggled menu bootstrap

I'm making a reusable toggle sidebar menu and cannot for the life of me get this glyphicon to be anchored to the left of the menu so it is always visible. Right now it looks like the first image in the album.
I'm using a template so I'm pretty sure that it's just some little css thing I haven't messed with, but my instructor was unable to fix it as well :O
When the menu is toggled, it looks like the second image. I would obviously like the glyphicon to be visible, showing that it can be toggled out again.
And I have one slightly other bad problem (the third image):
When the menu is toggled closed, there is a scrollbar across the bottom >.<
Here is a link to my html/js and css on JSFiddle
And the JS I use for toggling
<script>
$("#menu-toggle").click(function(e) {
e.preventDefault();
$("#wrapper").toggleClass("toggled");
});
</script>
I've created an update JsFiddle with it working here
It was the text-indent property and display block causing the issue.
I have changed your CSS to:
.sidebar-nav li a {
text-decoration: none;
color: #999999;
}
.sidebar-nav li.link a {
text-indent: 20px;
display: block;
}

How to reset a CSS attribute that's been changed using JavaScript?

I have navigation buttons that increase in width from 100px, to 150px when hovered over :
nav li:hover{
width:150px;
}
But using javascript I have made it so that which ever option has been selected, will continue to have a width of 150px. When each option is selected, it makes the other options go back to 100px as I intended :
function show1(){
document.getElementById("nav1").style.width="150px";
document.getElementById("nav2").style.width="100px";
document.getElementById("nav3").style.width="100px";
}
function show2(){
document.getElementById("nav2").style.width="150px";
document.getElementById("nav1").style.width="100px";
document.getElementById("nav3").style.width="100px";
}
function show3(){
document.getElementById("nav3").style.width="150px";
document.getElementById("nav1").style.width="100px";
document.getElementById("nav2").style.width="100px";
}
The problem is, once one of the navigation options has been selected, they no longer increase in width to 150px when hovered over, because the functions have set them to stay at 100px.
I am trying to work out how to make it so that each of the navigation buttons always increases in width when hovered over, while whichever one has been selected stays at the increased length. So i'm trying to find a way to reset the width value to how it is defined by my CSS after each function is executed.
Anyone know how to solve this? I'm fairly beginner level at javacript.
I would do this by putting the "selected" style in a separate CSS class, and dynamically adding that class to the objects you want to have the fixed width, then dynamically removing it.
Fiddling with CSS classes in JS is not very difficult; see here for example.
Make it an empty string and it takes over the one from your stylesheet again
document.getElementById("nav2").style.width = "";
You should make use of classes and forget about modifying styles using javascript, you can see how inconvenient it is. Consider this example and how it simplifies everything:
CSS:
nav li:hover,
nav li.selected {
width: 150px;
background: coral;
}
and JS:
var selected = document.getElementsByClassName('selected');
function show(obj) {
if (selected.length) selected[0].className = '';
obj.className = "selected";
}
Here we go. Instead of three duplicated functions showX you now have only one.
Demo: http://jsfiddle.net/zGfLP/
It can be further improved if you move get rid of the onclick handlers from HTML:
var nav = document.getElementById('nav'),
selected = nav.getElementsByClassName('selected');
nav.onclick = function(e) {
if (e.target.nodeName === 'LI') {
if (selected.length) selected[0].className = '';
e.target.className = "selected";
}
};
Demo: http://jsfiddle.net/zGfLP/1/

Interactive HTML webpage

EDIT: Thanks for a lot of great examples on how to solve these. I cant decide between who to accept yet, but I will go though all examples and see which I like the most. Great feedback guys! =D
I normally do these kind of things in flash, but this time it has to be compatible with mac, iPads and all those units too.
So, what do I need help with?
I've got a picture, with some "hotspots" on. I want to be able to click any of those hotspots to show some information.
This should be fairly basic and easy to achieve, but since I've never done this in html before I have to ask you guys =)
So, what would be the best way to do this? It have to be compatible with any browser and device, and it doesnt need to be very advanced. If it's possible to add effects to the box (sliding out, fading in, or anything like that) then thats a nice bonus, but not something I need.
Any help would be great!
BREAKDOWN:
I have a background image with some "hotspots" (numbers 1 and 2 in my example). The users should be able to either hover the mouse over any of these or click it to get more information, as seen in picture #2
This is that happens when you hover/click any of these hotspots.
Text and image is displayed inside a nice little info box.
If the user clicks "more information" it will open up even further to display more information if available. Like in this img:
I don't think the Javascript approach is really necessary here. I created a little CSS-only mock-up for you on JSBin.
Basically the point is that you enclose the image in a relatively positioned div, then absolute position the hotspots inside the same div. Inside the hotspots divs you will have the more info elements, showing only on :hover of their parents.
This makes it simple, and far more accessible.
Update: cropping the image equally from both sides
If you want to keep the image centered and still not use any javascript, you could set the required image as a background-image of the container, and setting its background-position parameters to center center.
You would have to make sure that the width of this div is set to the width of your image, and the max-width to 100%, so that when the window gets resized below the image width it stays at the center.
Now, a problem that I encountered here is how to make the hotspots stay center relatively to the image. I solved it this way:
I created a wrapper div for the hotspots with these characteristics:
margin: 0 auto;
position: relative;
width: 0px;
This basically makes sure that the wrapper div finds the center of our image. Then, you would position the hotspots relatively to the top-center position of the image, instead of the top-left as a starting point.
Then you have what you are looking for.
Working demo
Here's another approach, and in my opinion far superior to using a map or excessive JS. Place <div> elements on top of the element with the background-image and have HTML and CSS do the heavy lifting for you.
See it on JSFiddle
HTML
The HTML should seem pretty each enough to understand, we create <div>s with the class hotspot and rely on certain things being present. Namely .text (to show digit), .hover-popup (to show on hover) and .click-popup (which is inside .hover-popup and is shown when clicked).
<div id="hotspot1" class="hotspot">
<div class="text">1</div>
<div class="hover-popup">
I was hovered!
<div class="click-popup">
I was clicked on!
</div>
</div>
</div>
<div id="hotspot2" class="hotspot">
<div class="text">2</div>
<div class="hover-popup">
I was hovered!
<div class="click-popup">
I was clicked on!
</div>
</div>
</div>
CSS
This is where most of the magic happens, see the comments for further explanation.
/* These two position each hotspot */
#hotspot1 {
left:15%; /* we could use px or position right or bottom also */
top:20%;
}
#hotspot2 {
left:35%;
top:25%;
}
/* General styles on the hotspot */
.hotspot {
border-radius:50%;
width:40px;
height:40px;
line-height:40px;
text-align:center;
background-color:#CCC;
position:absolute;
}
.hotspot .text {
width:40px;
height:40px;
}
/* Show the pointer on hover to signify a click event */
.hotspot .text:hover {
cursor:pointer;
}
/* hide them by default and bring them to the front */
.hover-popup,
.click-popup {
display:none;
z-index:1;
}
/* show when clicked */
.hotspot.clicked .click-popup {
display:block;
}
/* show and position when clicked */
.hotspot:hover .hover-popup {
display:block;
position:absolute;
left:100%;
top:0;
width:300px;
background-color:#BBB;
border:1px solid #000;
}
JavaScript (with jQuery)
Unfortunately you're going to have to use some JavaScript for the clicking part as CSS doesn't have a 'clicked' state (outside of hacks with checkboxes). I'm using jQuery because it's dead easy to do what I want.
$(document).ready(function () {
$('.hotspot').click(function () {
$(this).toggleClass('clicked');
});
});
Creating the arrow
Over at css-tricks you can find a tutorial for attaching an arrow to a element using the :before and/or :after pseudo-elements. You can even 'simulate' a border around them by placing the :after element on top of the :before. But yea, lots of resources on how to do this.
You should be able to use the onclick or OnMouseOver event in the map area (define the href as "").
An example using OnMouseOver is here: http://www.omegagrafix.com/mouseover/mousimap.html
Give a class for that image in html (Ex: imgclass). And in javascript(using jquery), build that hover box in html format and bind it to 'mouseover' event of that image.
For example:
function bindhtmltoimage() {
myimg = $('body').find('.imgclass');
divSlot.each(function (index) {
$(this).bind('mouseover', function () {
try {
//position the hover box on image. you can customize the y and x axis to place it left or right.
var x = $(this).offset().left;
var y = $(this).offset().top;
var position = $(window).height() - ($("#divHover").height() + y);
var widthposition = $(window).width() - ($("#divHover").width() + x);
if (position < 0 || widthposition < 0) {
if (position < 0) {
$("#divHover").css({
position: 'absolute',
left: x + 20,
top: y - $("#divHover").height() - 20
});
}
if (widthposition < 0) {
$("#divHover").css({
position: 'absolute',
left: x - $("#divHover").width(),
top: y + 20
});
}
}
//build your html string for that hover box and apply to it.
$('#divHover').html("your Html content for that box goes here");
$('#divHover').show();
//if you want the box dynamically generated. create the html content and append to the dom.
}
catch (e) {
alert(e)
}
});
});
}
it will work fine in desktop and mobile. if you face any problem in touch devices, bind the function to click event instead of 'mouseover'.
Also, for map approach, i strongly recommend SVG instead of images.

Categories

Resources