Changing parent css on child focus (without breaking parent:hover css rules) - javascript

I made a menu on html (on the side and 100% heigth, expandeable as in android holo)
<div id="menu">
<button class="menubutton"></button>
<button class="menubutton"></button>
</div>
The menu normally remains transparent and with a short width:
#menu {
background-color: transparent;
width: 8%;
}
The idea was to expand and color it on hover. It was easy:
#menu:hover {
background-color: blue;
width: 90%;
}
There is no problem untill here. I need the same effect on focus. There is no way in css to change parent css on child focus (neither hover by the way, but it is not needed, cuase i can use the entire menu hover).
So i used a script:
var menubuttonfocus = document.getElementsByClassName("menubutton");
for (i=0; i<menubuttonfocus.length; i++) {
menubuttonfocus[i].addEventListener("focus", function() {
menu.style.backgroundColor = "blue";
menu.style.width = "90%";
});
menubuttonfocus[i].addEventListener("blur", function() {
menu.style.backgroundColor = "transparent";
menu.style.width = "8%";
});
}
The script works just fine, the problem is that when you trigger those events by focusing a button, the css of #menu:hover changes somehow and #menu does not change when hovering. I tried to solve this by doing something similar but with hover instead of focus:
menu.addEventListener("mouseenter", function(){
menu.style.backgroundColor = "blue";
menu.style.width = "90%";
});
menu.addEventListener("mouseout", function(){
menu.style.backgroundColor = "transparent";
menu.style.width = "8%";
});
This works somehow, but it is REALLY buggy.
I tried also to select "#menu:hover,#menu:focus", but it doesn't work because the focus is on the button elements and not in #menu.
Please avoid jquery if posible, and i know it's asking for too much but a pure css solution would be awesome.
Probably helpful info: html element are created dinamically with javascript.
I can show more code or screenshot, you can even download it (it is a chrome app) if needed: chrome webstore page
Thanks.
SOLVED: I did what #GCyrillus told me, changing #menu class on focus via javascript eventListener. .buttonbeingfocused contains the same css as "#menu:hover". Here is the script:
var menubuttonfocus = document.getElementsByClassName("menubutton");
for (i=0; i<menubuttonfocus.length; i++) {
menubuttonfocus[i].addEventListener("focus", function() {
menu.classList.add("buttonbeingfocused");
});
menubuttonfocus[i].addEventListener("blur", function() {
menu.classList.remove("buttonbeingfocused");
});
}

if the problem is what I think it is - you forgetting about one thing:
When you focusing / mouseentering the .menubutton - you are mouseleaving #menu and vice-versa - so your menu behaviour is unpredictible because you want to show your menu and hide it at the same time.
solution is usually setting some timeout before running "hiding" part of the script, and clearing this timeout (if exist) when running "showing" part.
it will be something like this:
var menuTimeout;
function showMenu() {
if (menuTimeout) clearTimeout(menuTimeout);
menu.style.backgroundColor = "blue";
menu.style.width = "90%";
}
function hideMenu() {
menuTimeout = setTimeout( function() {
menu.style.backgroundColor = "transparent";
menu.style.width = "8%";
}, 800);
}
//then add your listeners like you did - but put these functions as a handlers - like this:
menu.addEventListener("mouseenter", showMenu);
...
//in addition you need also "mouseenter" and "mouseleave" events handled on .menubuttons

Related

Add a style to html and body via onclick

I am having a problem with my hamburger menu, my background page scrolling when it is open. I noticed that if I add 'overflow: hidden' to the body and html, it fixes the problem. However, when I close the menu, the body is obviously still locked. How can I add javascript to cancel these styles from the body and html?
This is what I have tried:
<div class="navbar_toggle" onclick="nonscroll(this)">
<script>
function nonscroll(elem) {
document.body.style.overflow = "hidden";
document.html.style.overflow = "hidden";
}
</script>
When I click the button, I want these styles to apply. When I close the menu, I want them to disappear. Is there a very simple way to fix this?
Solution
Copy from this example, paste into your page:
function FixScrollToggle (node) {
var target = document.body;
function isClicked () {
return target.classList.contains('fixScrollToggle--on');
}
function freeze() {
target.classList.add('fixScrollToggle--on');
}
function unFreeze() {
target.classList.remove('fixScrollToggle--on');
}
function onClick (e) {
if (isClicked()) { return unFreeze(); }
return freeze();
}
node.addEventListener('click', onClick, false);
}
document.addEventListener('DOMContentLoaded', function () {
Array.prototype.slice.
call(document.querySelectorAll('[data-apply-fix]')).
forEach(FixScrollToggle);
});
.fixScrollToggle--on {
overflow: hidden;
background-color: #c00;
}
<div class="navbar_toggle" data-apply-fix>Click me</div>
It's not the most easy way, but it's a clean and relatively easy way. Just put the JS code into a <script> tag in your page.
Explanation
This code works by adding a click eventlistener to each HTML element which has a data-apply-fix attribute set. This way, you can attach the same behaviour to multiple elements on your page.
The behaviour of manipulating the CSS is entirely done with the CSS class fixScrollToggle--on. This way, you have a very clean separation of concerns.
In nonscroll() function, you need to do two things:
Check if nav is hidden or shown.
When you know the nav visibility, you can update style for body and html.
<div class="navbar_toggle" onclick="nonscroll(this)">
<script>
function nonscroll(elem) {
// check if navbar is hidden or shown
// if nav is hidden: "body overflow is 'hidden'"
// else "body overflow is 'static'"
var nav = document.getElementsByClassName("navbar_toggle");
var nav_hidden = (window.getComputedStyle(nav[0]).visibility === "hidden")
if(nav_hidden) {
document.body.style.overflow = "hidden";
document.html.style.overflow = "hidden";
} else {
document.body.style.overflow = "static";
document.html.style.overflow = "static";
}
}
</script>
Simple ways to do that is using toggle class on click listener, as your code above it could:
var toggle = document.getElementsByClassName('navbar_toggle')[0];
var html = document.getElementsByTagName('html')[0];
var body = document.getElementsByTagName('body')[0];
var all = [html,body];
toggle.addEventListener( 'click', function() {
for ( var i=0; i < all.length; i++ ) {
all[i]classList.toggle('is-active');
}
});
Then ur css should be:
html, body {
overflow: auto;
}
.is-active {
overflow: hidden;
position: fixed; // prevent body scrolling for safari and ios
}

firefox bug - Options disappeared on hover

Firefox represents a bug when I insert a select box on mouseenter event. The whole dropdown list is gone on hover. How can I fix this bug?
document.querySelector('#test').addEventListener('mouseenter',function(){
this.innerHTML = '<select><option value=1>2</option><option value=2>3</option></select>';
});
#test{
width: 100px;
height: 100px;
background-color: #000;
}
<div id="test"></div>
Here is an alternative approach where we can append to the <div> element. In this example, mouseenter may fire a lot, so I wrapped this code in a closure to execute once to demo appending once. You can surely craft this to your needs but this approach should allow a bit more functionality than overwriting the html of the element
JSFiddle Link
var append = (function(ele, node) { // execute once closure
var executed = false;
return function (ele, node) {
if (!executed) {
executed = true;
ele.appendChild(node);
}
};
})();
document.querySelector('#test').addEventListener('mouseenter', function() {
var that = this;
var node = document.createElement('select');
node.innerHTML = '<option value=1>2</option><option value=2>3</option>'
append(that, node);
});
I can't select a value in Chrome either so this wouldn't be just a Firefox bug I think.
Anyway here is a fix. Just load the select when it's not loaded on mouse enter.
document.querySelector('#test').addEventListener('mouseenter',function(){
if(this.innerHTML===""){
this.innerHTML = '<select><option value=1>2</option><option value=2>3</option></select>';}
});

script only firing on text link, not span

first post so go easy on my noobness!
I have a script that opens a popup window when some linked text is clicked, but now I want to get rid of the text and just use an empty span with background img as the link. Needless to say the script will not work with just the span and I'd appreciate some pointers on how to modify it to work (or suggest any workarounds if it ain't gonna work on an empty span).
Current link structure (which uses the text link to trigger the js window containing thelink.com):
<a href="http://thelink.com" class="pop">
<span class="icon_bg"><!-- empty span with image as background --></span>
some text here
</a>
Desired link structure (no text, just empty span with bg img):
<a href="http://thelink.com" class="pop">
<span class="icon_bg"><!-- empty span with image as background --></span>
</a>
Current script:
function popWin() {
function addEvent(element, eventName, callback) {
if (element.addEventListener) {
element.addEventListener(eventName, callback, false);
} else {
element.attachEvent("on" + eventName, callback);
}
}
function init() {
var links = document.querySelectorAll('a.pop');
    for (var i = 0; i < links.length; i++) {
popWin.addEvent(links[i], 'click', popWin.popup)
    }
}
function openPopup(e) {
var top = (screen.availHeight - 500) / 2;
var left = (screen.availWidth - 500) / 2;
var e = (e ? e : window.event);
    var target = (e.target ? e.target : e.srcElement);
var popup = window.open(
target.href,
'social',
'width=550,height=420,left='+ left +',top='+ top +',location=0,menubar=0,toolbar=0,status=0,scrollbars=1,resizable=1'
);
if(popup) {
popup.focus();
e.preventDefault();
return false;
}
return true;
}
return {
init: init,
popup: openPopup,
addEvent: addEvent
}}
var popWin = new popWin();
popWin.addEvent(window, 'load', popWin.init)
My hunch is to somehow define the span tag as the target using .nodeName. All help appreciated.
You can use the <span>&nbsp</span> to simulate invisible text and thus will cause the span to get width.
Your span is empty currently. Add some content to it, then it would work for you.
<span>Content</span>
Now the event would trigger for you.
Background image won't be visible until you have some width and height for the span. I would refer you to use
span {
/* change the display property of span; default is inline */
display: inline-block;
/* because width and height can be applied to only block level elements */
width: 100px;
height: 100px;
}
Now try it again. Have my fiddle, it makes a good use of your example, and it enables click on the textless span.
Also, you're not having any click handler in your code.
<span onclick="popWin()">
This would enable the click event.
http://jsfiddle.net/afzaal_ahmad_zeeshan/FdjZy/
OK, I think I've worked out the solution, but am curious if this is gonna be bombproof (seems to work in the few browsers I've tried so far).
Changed the line:
var target = (e.target ? e.target : e.srcElement)
To:
var target = (e.target ? e.target : e.srcElement).parentNode;
Now the span tag is triggering the script without the need for any text anywhere within the a tags.

How do I select an element inside of a div using Jquery

My Jquery isn't working with the way I'm selecting the <p> and <img> elements. How could I get it to work?
function projectanim(x)
{
var Para = x.getElementsByTagName("p");
var Imgs = x.getElementsByTagName("img");
if ($(x).height() != 200)
{
$(x).animate({height:'200px'});
$(Para[0]).animate({display:'inline'});
$(Imgs[0]).animate({display:'inline'});
}
else
{
$(x).animate({height:'25px'});
$(Para[0]).animate({display:'none'});
$(Imgs[0]).animate({display:'none'});
}
}
Without the HTML this is just a shot in the dark but I assume you're trying to get the paragraph and image in a specific div?
Try this:
var Para = x.find("p");
var Imgs = x.find("img");
Although depending on what you're actually passing as x will determine whether it will actually work...
function projectanim (projectId) {
$('p, img', $('div#' + projectId)) // Select p/img tags children of <div id="projectId">
.slideDown(); // show using slideDown, fadeIn() or show('slow')
}
// Example
projectanim ('protflolio_project');
The idea with jQuery is:
Use the right selectors
With the right methods
Examples
Different ways to select all img and p tags under a div which id is my_div:
// The easy way
p_and_img = $('#my_div p, #my_div img');
// Using the context parameter
p_and_img = $('p, img', $('#my_div'));
// Using the context parameter and making sure my_div is a div
p_and_img = $('p, img', $('div#my_div'));
// only the first p and img
p_and_img = $('p:eq(0), img:eq(0)', $('#my_div'));
Your question is really, really vague, but from what I can gather, this is what you're looking at achieving:
function projectanim(x) {
var self = $(x);
if (self.height() === 200) {
self.animate({ height : '25px' })
.find('p,img').fadeOut()
;
} else {
self.animate({ height : '200px' })
.find('p,img').fadeIn()
;
}
}
That being said though, barring browser compatibility and all that shizz, you really should be doing something like this using CSS more than Javascript.
Depending on your parent element (say, a <div>), you can write up CSS like the following:
div {
height : 200px;
transition : height .5s linear;
}
div.active {
height : 25px;
}
div img,
div p {
display : inline;
opacity : 100;
transition : opacity .5s linear;
}
div.active img,
div.active p {
opacity : 0;
}
and just toggle a class on/off with your Javascript:
function projectanim(x) {
$(x).toggleClass('active');
}
and everything should be automatic. Your Javascript becomes waaaaaay simpler, less coupled, more maintainable, and your styles are right where they should be (in CSS files).
"what i'm trying to do is fade the and to inline from none."
Do you just want and to fade in? Or do you want it to go from display:none to inline and fade in?
I'll show you how to do both, and you can take away parts if you just want the fade in feature.
First off set p, and img as display:none; and opacity:0, in the css like so
p, img
{
display:none;
opacity:0;
}
Secondly your js has to alter the display of both , and tags and fade in/out like so.
function projectanim(x)
{
if ($(x).height() != 200)
{
$(x).animate({height:'200px'});
document.getElementsByTagName("p").style.display = 'inline';
document.getElementsByTagName("img").style.display = 'inline';
$("p").animate({"opacity": "1"}, 1000);
$("img").animate({"opacity": "1"}, 1000);
}
else
{
$(x).animate({height:'25px'});
$("p").animate({"opacity": "0"}, 500);
$("img").animate({"opacity": "0"}, 500);
document.getElementsByTagName("p").style.display = 'none';
document.getElementsByTagName("img").style.display = 'none';
}
}

How to disable scrolling in outer elements?

I have a vertically-scrolling div within a page that also scrolls vertically.
When the child div is scrolled with the mouse wheel and reaches the top or bottom of the scroll bar, the page (body) begins to scroll. While the mouse is over the child div, I'd like the page (body) scroll to be locked.
This SO post (scroll down to the selected answer) demonstrates the problem well.
This SO question is essentially the same as mine, but the selected answer causes my page contents to noticeably shift horizontally as the scrollbar disappears and reappears.
I thought there might be a solution that leverages event.stopPropagation(), but couldn't get anything to work. In ActionScript, this kind of thing would be solved by placing a mousewheel handler on the child div that calls stopPropagation() on the event before it reaches the body element. Since JS and AS are both ECMAScript languages, I thought the concept might translate, but it didn't seem to work.
Is there a solution that keeps my page contents from shifting around? Most likely using stopPropagation rather than a CSS fix? JQuery answers are welcome as is pure JS.
here's what i ended up with. very similar to #mrtsherman's answer here, only pure JS events instead of jQuery. i still used jQuery for selecting and moving the child div around, though.
// earlier, i have code that references my child div, as childDiv
function disableWindowScroll () {
if (window.addEventListener) {
window.addEventListener("DOMMouseScroll", onChildMouseWheel, false);
}
window.onmousewheel = document.onmousewheel = onChildMouseWheel;
}
function enableWindowScroll () {
if (window.removeEventListener) {
window.removeEventListener("DOMMouseScroll", onArticleMouseWheel, false);
}
window.onmousewheel = document.onmousewheel = null;
}
function onChildMouseWheel (event) {
var scrollTgt = 0;
event = window.event || event;
if (event.detail) {
scrollTgt = -40 * event.detail;
} else {
scrollTgt = event.wheelDeltaY;
}
if (scrollTgt) {
preventDefault(event);
$(childDiv).scrollTop($(childDiv).scrollTop() - scrollTgt);
}
}
function preventDefault (event) {
event = event || window.event;
if (event.preventDefault) {
event.preventDefault();
}
event.returnValue = false;
}
i've noticed the scrolling doesn't match normal scrolling exactly; it seems to scroll a bit faster than without this code. i assume i can fix by knocking down wheelDeltaY a bit, but it's odd that it would be reported differently by javascript than it's actually implemented by the browser...
I usually do it with a small hack listening to the scroll event on the document: it resets the scroll height back to the original one - effectively freezing the document from scrolling but any inner element with overflow: auto will still scroll nicely:
var scrollTop = $(document).scrollTop();
$(document).on('scroll.scrollLock', function() {
$(document).scrollTop(scrollTop);
});
and then when I'm done with the inner scroll lock:
$(document).off('scroll.scrollLock');
the .scrollLock event namespace makes sure I'm not messing with any other event listeners on scroll.
Although this is an old question, here is how I do it with jQuery. This allows you to scroll a list within an outer list, or you can change the outer list to the document to do what the OP asked.
window.scrollLockHolder = null;
function lockScroll(id){
if (window.scrollLockHolder == null){
window.scrollLockHolder = $('#' + id).scrollTop();
}
$('#' + id).on('scroll', function(){
$('#' + id).scrollTop(window.scrollLockHolder);
});
}
function unlockScroll(id){
$('#' + id).off('scroll');
window.scrollLockHolder = null;
}
And you can use it like this:
<ul onmousemove="lockScroll('outer-scroller-id')" onmouseout="unlockScroll('outer-scroller-id')">
<li>...</li>
<li>...</li>
</ul>
what about this:
div.onmousemove = function() { // may be onmouseover also works fine
document.body.style.overflow = "hidden";
document.documentElement.style.overflow = "hidden";
};
div.onmouseout = function() {
document.body.style.overflow = "auto";
document.documentElement.style.overflow = "auto";
};

Categories