The image is not changing when it collapses back (jquery, css) - javascript

The image will change when it expands, but when it collapses back, it won't change, my code looks like below:
JS:
(function ($) {
$(document).ready(function () {
// Categories menu opening
$('.woocommerce.widget_product_categories .product-categories li.cat-parent').prepend('<div class="cat-menu-close"></div>');
$(document).on("click", ".woocommerce.widget_product_categories .product-categories li.cat-parent > .cat-menu-close", function (e) {
var $catParent = $(this).closest('li.cat-parent');
var state = $catParent.hasClass('close');
$catParent.toggleClass('opened', !state);
$(this).nextAll('ul.children:first').slideToggle(state);
});
});
})(jQuery);
CSS:
.woocommerce.widget_product_categories .product-categories li .cat-menu-close {
position: absolute;
right: 0;
line-height: 20px;
text-align: center;
cursor: pointer;
top: 4px;
width: 20px;
height: 20px;
}
.woocommerce.widget_product_categories .product-categories li > .cat-menu- close:hover {
opacity: 0.5;
}
.woocommerce.widget_product_categories .product-categories li > .cat-menu- close:after {
display: inline-block;
margin-left: 2px;
background: url("../img/arrow-right.svg") no-repeat center center;
background-size: 20px 20px;
width: 10px;
height: 20px;
content: "";
}
.woocommerce.widget_product_categories .product-categories li.opened > .cat- menu-close:after {
background: url("../img/arrow-down.svg") no-repeat center center;
background-size: 20px 20px;
}
Could someone help? I need to change the image when it expands and collapses, thank you and much appreciated.

the issue is that the category even after the SlideUp stays like this
class="cat-item cat-item-69 cat-parent opened"
instead of returning to it's original state that is
class="cat-item cat-item-69 cat-parent"
I am trying to find out the reason myself but I have too little experience with js so it's kinda hard to figure it out.
now if you remove the "!" from
$catParent.toggleClass('opened', !state);
the image will stop moving in general as it will not change the state.
edited answer bellow
Alright I have solved the riddle
After a bit of trial and error, I have your answer. In your code you have this:
var $catParent = $(this).closest('li.cat-parent');
var state = $catParent.hasClass('close');
$catParent.toggleClass('opened', !state);
this part of the code sets the starting state "close"
var state = $catParent.hasClass('close');
and when you click you get the state "opened" with the
$catParent.toggleClass('opened', !state);
This doesn't give you the option to change back to the original state onclick as the "opened" state is set to as important with the exclamation mark.
Now if you edit your code to set two variable states as opened and close. and then leave the toggleClass to change between the states you get the outcome you want. I am not sure if my explanation is correct but here is the code you need.
I have already tested it and I am using it on the webpage I am administrating if you want you can check it out
// Categories menu opening
$('.woocommerce.widget_product_categories .product-categories li.cat-parent').prepend('<div class="cat-menu-close"></div>');
$(document).on("click", ".woocommerce.widget_product_categories .product-categories li.cat-parent > .cat-menu-close", function(e) {
var $catParent = $(this).closest('li.cat-parent');
var state = 'close'
var state = 'opened'
$catParent.toggleClass(state);
$(this).nextAll('ul.children:first').slideToggle(state);
[Edit]
To anyone checking out this answer I would better recommend another plugin named 'WooCommerce Product Categories Selection' as it seems to be functioning way better.
I am using it on my websites now, the only downside is that it doesn't have icons for expand and collapse but it's really easy to add it, and if you do decide to go that way you may like find this solution helpfull

Related

JS Hover Over One Item to Make Another Move

I've got a simple text button with an image of an arrow next to it. I'm wanting the arrow image to move when someone hovers over the button.
I currently have this working in one instance with JS 'document.getElementById...', but I have several buttons across my site that I'd like to have the same behavior. My first thought would be to use a class instead of an id, and use the same functions.
For whatever reason, document.getElementsByClassName doesn't work - even in one instance.
Here's a simpler version to demonstrate - View on Codepen: https://codepen.io/sdorr/pen/JxYNpg
HTML
<HTML>
hover over me
<div id="block"></div>
hover over me
<div class="block"></div>
CSS
* {
margin: 0;
padding: 0;
}
.button {
color: #000000;
text-decoration: none;
background-color: cyan;
margin: 0;
display: block;
width: 300px;
padding: 20px;
text-align: center;
}
#block {
width: 30px;
height: 30px;
background-color: red;
}
.block {
width: 30px;
height: 30px;
background-color: green;
}
JS
function move() {
document.getElementById("block").style.marginLeft = "35px";
}
function moveBack() {
document.getElementById("block").style.marginLeft = "0px";
}
function moveAlt() {
document.getElementsByClassName("block").style.marginLeft =
"35px";
}
function moveBackAlt() {
document.getElementsByClassName("block").style.marginLeft =
"0px";
}
First off, why isn't the behavior with a class working but an id works fine?
Secondly, would a class solve this issue and be scalable across all buttons with the same two functions (onmouseover / onmouseout)?
If not, any ideas on a solution? I currently have a solution I found using jQuery that does work, but when hovering over one button, all arrow images move across the site. I don't necessarily mind this behavior because only one button is really in view at a time - but I'm trying to learn JS and solve problems with my own solutions!
I greatly appreciate your desire to learn on your own and not rely on premade solutions. Keep that spirit and you will go places!
When it comes to getElementsById, we know this should work for one element, since the function returns a single Element.
However, what does getElementsByClassName return?
(see: https://developer.mozilla.org/en-US/docs/Web/API/Document/getElementsByClassName)
It returns an HTMLCollection which you can iterate over to change an single element's style.
So, to get this to work with JavaScript you need to write a function that will be able to identify the particular div.block you want to move. But, this puts you back to where you started, needing some particular identifier, like an id or a dataset value to pass to the function.
Alternately, based on the HTML structure you provide, you could look for nextElementSibling on the a that get's clicked. But I would set up an eventListener rather than adding a JS function as a value to the onmouseenter property.
const btns = document.getElementsByTagName('a');
/*** UPDATE forEach is a NodeList method, and will fail on HTMLCollection ***/
/* this fails -> Sorry! ~~btns.forEach(button=>{~~
/* the following will work
/**********/
for (let i = 0; i < btns.length; i++){
btns[i].addEventListener('mouseenter', function(e) {
//we pass e to the function to get the event and to be able to access this
const block = this.nextElementSibling;
block.style.marginLeft = "35px";
})
btns[i].addEventListener('mouseleave', function(e) {
const block = this.nextElementSibling;
block.style.marginLeft = "0";
})
}
But with siblings, there is a CSS-only solution.
We can use the Adjacent Sibling Selector combined with the :hover state selector and no JavaScript is needed, if we are just moving back and forth.
.button:hover+.block {
margin-left: 35px;
}
See the Snipped Below
* {
margin: 0;
padding: 0;
}
.button {
color: #000000;
text-decoration: none;
background-color: cyan;
margin: 0;
display: block;
width: 300px;
padding: 20px;
text-align: center;
}
.block {
width: 30px;
height: 30px;
background-color: green;
}
.button:hover+.block {
margin-left: 35px;
}
hover over me
<div class="block"></div>
hover over me
<div class="block"></div>
As Vecta mentioned, getElementsByClassName returns an array-like. You'll need to do something like this to get the first element:
function moveAlt() {
document.getElementsByClassName("block")[0].style.marginLeft = "35px";
}
function moveBackAlt() {
document.getElementsByClassName("block")[0].style.marginLeft = "0px";
}
However a better solution might be to use document.querySelector, which operates similarly to jQuery's $() syntax:
function moveAlt() {
document.querySelector(".block").style.marginLeft = "35px";
}
function moveBackAlt() {
document.querySelector(".block").style.marginLeft = "0px";
}

check slideToggle state with if else function & report slideToggle state after each toggle

http://jsfiddle.net/vmKVS/
I am trying to understand the state of slideToggle with a little example.
When I toggle the first time the options element turns green.
I would expect the menu element to turn red when I click toggle again, now the 2nd time, since now the options element goes into not visible state and therefore the else part of the function should run, no?
Also when I check the console the values for the slideToggle state do not change, why? Should they not reflect the state of the element after the click second click, so hidden?
What am I doing wrong? I would like to have for the sake of this example, turn the menu red on the second toggle, since then the options element is not visible any more.
I am new to jQuery and programming, so please forgive my blatant mistakes in logic here. Perhaps I have to rethink this from a different viewpoint. This is where you come in, help me understand this please. Thank you.
HTML
<div id="menu">MENU</div>
<div id="options">OPTIONS</div>
jQuery
$('#menu').click(function(){
$('#options').slideToggle();
var isVisible = $( '#options' ).is( ":visible" );
var isHidden = $( '#options' ).is( ":hidden" );
console.log (isVisible);
console.log (isHidden);
if ($('#options').is(':visible') === true ){
$('#options').css('background', '#16a085');
console.log (isVisible);
}
else {
console.log (isHidden);
$('#menu').css('background', '#8e44ad');
}
});
CSS
#menu {
width: 100%;
height: 100px;
line-height: 100px;
font-size: 200%;
background: #000000;
color: #fff;
text-align: center;
}
#options {
width: 100%;
height: 100px;
line-height: 100px;
font-size: 200%;
background: #ccc;
color: #fff;
text-align: center;
display: none;
}
Check out the documentation for the jQuery :visible and :hidden selectors more thoroughly:
http://api.jquery.com/visible-selector/
http://api.jquery.com/hidden-selector/
The issue is that the :visible selector will return true if the element that is selected consumes space within the layout, and as the width is not zero, this is true.
There are plenty of ways to do what you're after. A nice way is to toggle a class and use that to detect whether the item is toggled:
$('#menu').click(function(){
$('#options').slideToggle().toggleClass('opened');
var isVisible = $( '#options' ).is( ".opened" );
if (isVisible === true ){
$('#options').css('background', '#16a085');
}
else {
$('#menu').css('background', '#8e44ad');
}
});
http://jsfiddle.net/vmKVS/1/

changing css background image in collapsible div using javascript/jQuery

I’m after a hand with a bit of JavaScript if possible, I’m working on a collapsible list using jQuery and want to change a background image in a css file dependent on the state of the list
This is the html for the div
<div class="collapse_div">
<div class="header_div">header text</div>
<div class="content_div">
Some text
</div>
<div class="header_div">another header</div>
<div class="content_div">
some more text
</div>
</div>
This is the .css that puts the image (expanded.gif) into the header div
.collapse_div{
margin: 0;
padding: 0;
width: 500px;
}
.header_div {
margin: 1px;
color: #000;
padding: 3px 10px;
cursor: pointer;
position: relative;
background: url(expanded.gif) no-repeat 95%;
background-color:#ccc;
}
.content_div {
padding: 5px 10px;
background-color:#fafafa;
}
And this is the javascript function that controls the expand/collapse when header_div is clicked
jQuery(document).ready(function() {
jQuery(".content_div").hide();
//toggle the componenet with class msg_body
jQuery(".header_div").click(function()
{
jQuery(this).next(".content_div").slideToggle(500);
});
});
I’ve played around with adding code to the .click(function) to try and change the background css tag in .header_div to another file (collapse.gif) but I can’t get it to work, so I thought I’d ask the experts as my javascript is really rusty
At the moment the collapse/expand of the div works fine having the background image change on click would really make it look good
You can have a class with the requried background set and apply that class conditionally. Try this
CSS
.header_div_collapsed {
background: url(collapse.gif) no-repeat 95% !important;
}
JS
jQuery(document).ready(function() {
jQuery(".content_div").hide();
//toggle the componenet with class msg_body
jQuery(".expand_div").click(function()
{
jQuery(this).next(".content_div").slideToggle(500, function(){
var $this = $(this);
if($this.is(':visible')){
$this.removeClass('header_div_collapsed');
}
else{
$this.addClass('header_div_collapsed');
}
});
});
});
Your script should be something like this,
jQuery(document).ready(function() {
jQuery(".content_div").hide();
//toggle the componenet with class msg_body
jQuery(".expand_div").click(function()
{
var elm = jQuery( this );
jQuery(this).next(".content_div").slideToggle(500, function(){
if(jQuery(this).is(":visible"))
jQuery(elm).css({"background-image" : "collapse.gif"});
else
jQuery(elm).css({"background-image" : "expand.gif"});
});
});
});
thanks to both the suggestions post below I managed to get this to work
firstly I added a whole new css function as just defining the background didn't work
.expand_div_collapsed {
margin: 1px;
color: #000;
padding: 3px 10px;
cursor: pointer;
position: relative;
background: url(collapsed.gif) no-repeat 95%;
background-color:#ccc;
}
then the JS was changed to this
jQuery(document).ready(function() {
jQuery(".content_div").hide();
//toggle the componenet with class msg_body
jQuery(".header_div").click(function()
{
var co = jQuery(this);
jQuery(this).next(".content_div").slideToggle(500, function(){
if(jQuery(this).is(':visible')){
jQuery(co).addClass('expand_div_collapsed');
}
else{
jQuery(co).removeClass('expand_div_collapsed');
}
});
});
});
the add and remove class calls had to be swapped around and I had to define the var co before the slideToggle call
but thanks to everyone who offered suggestions as I would have never got this to work otherwise

DIV menu ontop of <li> on hover [closed]

It's difficult to tell what is being asked here. This question is ambiguous, vague, incomplete, overly broad, or rhetorical and cannot be reasonably answered in its current form. For help clarifying this question so that it can be reopened, visit the help center.
Closed 9 years ago.
Hey guys, this is my first time actually posting at stackflow but i've used people's answers before. Really a great site.
Anywho onto my problemo. I have some 'li' tags. When I hover the mouse over these 'li's, I need a DIV to appear over the 'li' with some buttons, etc. Basically it's kind of a menu. the 'li's are an unpredictable length, usually somewhere from 1 line to 5.
A great example of what I'm trying to accomp is the dribbble.com homepage. Hover over an image (though I'm using 'li's) and a nifty lil info thing comes up.
I have absolutely no experience with javascript or jqry, I'm just a PHP guy with some CSS. I do the back-end work. Anywho, can anyone show me how to do this and include a basic example please? Would really really appreciate it.
Help?
Lets say you have the following structure:
<ul id="myMenu">
<li><div class="inactive">Menu 1</div><div class="active">..some icons..</div></li>
<li><div class="inactive">Menu 2</div><div class="active">..some icons..</div></li>
<li><div class="inactive">Menu 3</div><div class="active">..some icons..</div></li>
</ul>
This is a very basic menu and I'm sure you'll have something more complicated, but by using wrapped divs this will make setting up your structure much easier.
Your CSS would look something like this:
/* Initially hide rollover content */
#myMenu li div.active {
display: none;
/* Use the following if you want to overlay, otherwise delete */
z-index: 2;
position: absolute;
top: 0px; left: 0px;
}
And assuming you're using jQuery the javascript would look something like this:
// DOM Ready
jQuery(function($) {
// Reference to menu
$("#myMenu").delegate("li", "mouseenter mouseleave", function(evt) {
var $this = $(this);
switch(evt.type) {
// When the client mouses over
case "mouseover":
// To swap content use this
$this.find(".inactive").hide(); // Remove this line to overlay
$this.find(".active").show();
break;
// When the client mouses out
case "mouseout":
// To swap content use this
$this.find(".inactive").show(); // Remove this line to overlay
$this.find(".active").hide();
break;
}
});
});
Edit: had a typo in the css and the javascript, sorry bro
Create a .js file and paste this code in.
(function($) {
$.fn.tooltip = function(options) {
var
defaults = {
background: '#e3e3e3',
color: 'black',
rounded: false
},
settings = $.extend({}, defaults, options);
this.each(function() {
var $this = $(this);
var title = this.title;
if ($this.is('a') && $this.attr('title') != '') {
this.title = '';
$this.hover(function(e) {
// mouse over
$('<div id="tooltip" />')
.appendTo('body')
.text(title)
.hide()
.css({
backgroundColor: settings.background,
color: settings.color,
top: e.pageY + 10,
left: e.pageX + 20
})
.fadeIn(350);
if (settings.rounded) {
$('#tooltip').addClass('rounded');
}
}, function() {
// mouse out
$('#tooltip').remove();
});
}
$this.mousemove(function(e) {
$('#tooltip').css({
top: e.pageY + 10,
left: e.pageX + 20
});
});
});
// returns the jQuery object to allow for chainability.
return this;
}
})(jQuery);
This should go in your page;
<script src="../../Scripts/jQuery.tooltip.js" type="text/javascript"></script>
<style>
#tooltip {
background: url(../images/search.png) no-repeat 5px 50%;
border: 1px solid #BFBFBF;
float: left;
font-size: 12px;
max-width: 160px;
padding: 1em 1em 1em 3em;
position: absolute;
}
.rounded {
-moz-border-radius: 3px;
-webkit-border-radius: 3px;
}
</style>
<script type="text/javascript">
$('.tooltip').tooltip();
</script>
This is the link that will give you a tooltip;
over the years
Now, you can replace the <a> with anything you want so long as you have the class "tooltip". Then you can place buttons etc inside it.
This isn't the whole solution but it should get you pretty close.

Disabling browser tooltips on links and <abbr>s

I want to suppress the web browser's default tooltip display when a user hovers over certain links and elements. I know it's possible but I don't know how. Can anyone help?
The reason for this is to suppress the tooltip for microformatted date-times. The BBC dropped support for hCalendar because the appearane of the machine-readable date was an accessibility issue for those with cognitive disabilities aswell as some screen reader users. http://www.bbc.co.uk/blogs/bbcinternet/2008/07/why_the_bbc_removed_microforma.html
EDIT:
I whipped up a jquery plugin along the same lines as Aron's suggestion...
// uFsuppress plugin v1.0 - toggle microformatted dates
(function($){
$.ufsuppress = function() {
$(".dtstart,.dtend,.bday").hover(function(){
$(this).attr("ufdata",$(this).attr("title"));
$(this).removeAttr("title");
},function(){
$(this).attr("title",$(this).attr("ufdata"));
$(this).removeAttr("ufdata");
});
}
})(jQuery);
// Usage
$.ufsuppress();
As far as I know it is not possible to actually suppress showing the title tag.
There are some workarounds however.
Assuming you mean you want to preserve the title property on your links and elements, you could use Javascript to remove the title property at onmouseover() and set it again at onmouseout().
// Suppress tooltip display for links that have the classname 'suppress'
var links = document.getElementsByTagName('a');
for (var i = 0; i < links.length; i++) {
if (links[i].className == 'suppress') {
links[i]._title = links[i].title;
links[i].onmouseover = function() {
this.title = '';
}
links[i].onmouseout = function() {
this.title = this._title;
}
}
}
Add this element to your html
onmouseover="title='';"
For example i have a asp.net checkbox I store a hidden variable but do not want the user to see on as the tooltip.
Ran across this thread when using the jQuery plugin timeago. Actually the solution is very simple using the CSS property pointer-events. Posting this for the benefit of people coming here through a search engine :)
.suppress {
pointer-events:none;
}
Note that you shouldn't use this for things like links that should click through to something. In this case use the accepted JS solution.
Something like this in prototype would blank all title attributes of datetime microformats with a class of 'dtstart':
$$('abbr.dtstart').each(function(abbr){abbr.title=' '})
Note I used a blank space, the Mozilla documentation for element.title states
According to bug 264001 , setting
title to the empty string triggers the
default inheriting behavior. To cancel
inheritance, title must be set to a
non-empty whitespace string.
This won't help with your problem but might be interesting nevertheless: There's another universal attribute apart from title which can be used to store data - lang!
Just convert the data you want to store to a continuous string and prefix it with 'x-' to declare private usage in accordance with RFC 1766.
In the comments, sanchothefat clarified that he wants to solve the usability-issues with the abbr-design-pattern in microformats. But there are other patterns which are as semantically meaningful (or, in my opinion even more so) than this pattern. What I'd do:
<p>
The party is at
<dfn class="micro-date">10 o'clock on the 10th
<var>20051010T10:10:10-010</var></dfn>.
</p>
together wtih these styles
dfn.micro-date {
font-weight: inherit;
font-style: inherit;
}
dfn.micro-date var {
display: none;
}
In my opinion, the semantically most correct way would be to use a dl definition list - which isn't allowed inside of paragraphs. This can be worked around with the following pattern:
<p>
The party is at <q cite="#micro-dates">10 o'clock on the 10th</q>.
</p>
<dl id="micro-dates">
<dt>10 o'clock on the 10th</dt>
<dd>20051010T10:10:10-010</dd>
</dl>
which requires a more sophisticated stylesheet:
q[cite='#micro-dates']:before {
content: '';
}
q[cite='#micro-dates']:after {
content: '';
}
dl#micro-dates {
display: none;
}
This is what i did.
$('.fancybox').hover(
function(){
$(this).attr('alt',$(this).attr('title'));
$(this).attr('title','');
},
function(){
$(this).attr('title',$(this).attr('alt'));
$(this).removeAttr('alt');
}
).click(function(){
$(this).attr('title',$(this).attr('alt'));
$(this).removeAttr('alt');
});
You can hook the 'mouseenter' event and return false which will stop the native tooltips from being displayed.
$(selector).on( 'mouseenter', function(){
return false;
});
It's possible to suppress this behaviour with jQuery
var tempTitle;
$('[title]').hover(
function(e) {
debugger;
e.preventDefault();
tempTitle = $(this).attr('title');
$(this).attr('title', '');
// add attribute 'tipTitle' & populate on hover
$(this).hover(
function() {
$(this).attr('tipTitle', tempTitle);
}
);
},
// restore title on mouseout
function() {
$(this).attr('title', tempTitle);
}
);
.progress3 {
position: relative;
width: 100px;
height: 100px;
background: red;
}
.progress3:hover:after {
background: #333;
background: rgba(0, 0, 0, .8);
border-radius: 5px;
bottom: 26px;
color: #fff;
content: attr(data-tooltip);
left: 20%;
padding: 5px 15px;
position: absolute;
z-index: 98;
width: 220px;
}
.progress3:hover:before {
border: solid;
border-color: #333 transparent;
border-width: 6px 6px 0 6px;
bottom: 20px;
content: "";
left: 50%;
position: absolute;
z-index: 99;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.0.0/jquery.min.js"></script>
<div title='abc' data-tooltip="This is some information for our tooltip." class="progress3">
title='abc' will not be displayed
</div>
fiddle

Categories