Turn a function on or off after scrolling to bottom - javascript

I have a div with "Terms and Conditions". It is a vertically scrollable div. The idea is to scroll all the way down which will allow a user to then Submit (by hitting the submit button). But it is not really a button, but an anchor with a javascript function inside.
<a onclick="showDialog();"><img
name="continue" value="Continue" alt="Continue"
src="${resource(dir: 'images/buttons', file: 'submit.gif')}"
border="0" /></a>
So what I need to do is when a user scrolls down, it will then turn on the javascript function but it will remain off if not.
Any advice?

The scrollTop of the div (#terms) is how far down inside the top of the viewable area is. The height of the div is how much the user sees. It thus follows that the scrollTop of the div plus the height of the div must be at least as much as the total height of the entire terms of service document inside (which we will call #termsInner).
Here's some example HTML code: (Note: You can try this out at http://jsfiddle.net/8U7GY/6/.)
<p id="instructions">Please read these terms and conditions now.
<b>You must scroll to the bottom before continuing.</b></p>
<div id="terms">
<div id="termsInner">
<p>Lorem ipsum...</p>
</div>
</div>
<span><a id="submit">Register me!</a></span>
Some CSS code:
p {
padding: 0.25em;
}
#terms {
border: solid 1px;
height: 24em;
overflow: auto;
margin-bottom: 1em;
}
#termsInner {
padding: 0.5em 0;
}
.highlighted {
background-color: #ff0;
}
#submit {
color: blue;
text-decoration: underline;
}
And some JavaScript code: (This must be in $(function() { ... }); so that it is executed only once the document is ready.)
// Select the elements of the HTML page.
var instructions = $('#instructions'),
terms = $('#terms'),
termsInner = $('#termsInner'),
submit = $('#submit');
// Bind an event handler that will run when the user
// has not scrolled through the terms of service.
submit.bind('click', function() {
// Highlight the instructions.
instructions.addClass('highlighted');
// Remove the highlighting after two seconds.
setTimeout(function() {
instructions.removeClass('highlighted');
}, 2000);
});
// Once the user scrolls through, call showDialog instead
// when the user clicks.
terms.scroll(function() {
if (terms.scrollTop() + terms.height() >= termsInner.height()) {
submit.unbind('click').bind('click', showDialog);
}
});
// This is where you would continue the user registration process.
function showDialog() {
alert('whatever');
}
There are several important things to notice.
We do not use the onclick HTML attribute but rather bind event handlers programmatically using bind and unbind. This allows us to make a different thing happen depending on whether the user has scrolled down or not.
jQuery provides the scroll method to register a function to run whenever the user scrolls the div. We check the sum of scrollTop and height against the height of the content inside the div, and if that is OK, we unbind the original click handler and bind a new one.
We highlight the instructions (if the user has not yet scrolled down but clicks the anchor anyways) for usability. If the user clicks and finds that nothing happens, he would think that the registration form does not work, would leave your site, and be left with a bad experience.
Edit: Fixed it to work on Internet Explorer. Because of the way IE works, you cannot have padding set on the div #terms itself, so set any padding on #termsInner instead.

You need to check the if the <div>'s scrollTop+height is equal to the scrollHeight.
With jQuery:
$('a').click(function(){
var myDiv = $('div')
if (myDiv.scrollTop() + myDiv.height() == myDiv.get(0).scrollHeight)
{
showDialog();
}
else
{
return false;
}
});
You can also check the <div>'s scrollTop+height on the scroll event, and then enable/disable the anchor depending on whether or not the scrollHeight has been reached.

Related

How long does it take until click event is triggered?

I try to make a select box whose entries are opened after clicking into the input box. After selecting one of the items, the dropdown should be closed again.
I want to achieve the open/close part of the dropdown without the use of javascript.
The html looks like this:
<div id="outer">
<input type="text" id="input">
<div id="results">
<div>Test 1 </div>
<div>Test 2 </div>
<div>Test 3 </div>
<div>Test 4 </div>
</div>
</div>
<div id="label">
</div>
After clicking onto an item, the selected value should appear below the #outer div (just for demonstration purposes).
The Javascript for assigning click events to the dropdown values:
document.querySelectorAll("#results div").forEach(setClick);
function setClick(node) {
node.addEventListener("click", setText.bind(null, node.innerHTML))
}
function setText(t) {
document.getElementById("label").innerHTML = t;
}
Now I will show you my first draft of css code:
#outer {
width: 200px;
position: relative;
}
#input {
width: 100%;
}
#results {
position: absolute;
width: 100%;
display: block;
visibility: hidden;
background-color: white;
}
#results > div:hover {
background-color: lightblue;
cursor: pointer;
}
#outer:focus-within #results, #results:hover {
visibility: visible;
}
This works like a charm but fails in one point:
After clicking an item, the dropdown is not closed. This is because of the #results:hover selector which is needed to keep the dropdown open after clicking onto an item. The click takes the focus out of the input field, thus the focus-within selector is not applied anymore. As the focus is removed from the input before the click occurs, the dropdown is hidden when the final click arrives in the document (this is my understanding of the problem).
Thus I use the hover selector which forces the div to keep open as long as the mouse is above the div.
You can test this here:
https://jsfiddle.net/hcetz1og/3/
My solution for this was a transition that hides the dropdown after the focus has been taken away:
#outer:not(:focus-within) #results:hover {
visibility: hidden;
transition-property: visibility;
/*use 10 ms and the clicked value in the drop down won't be shown */
transition-delay: 100ms;
transition-timing-function: step-end;
}
This works on my machine when I use 100ms as a delay. If I use 10ms, I have the same problem again. It seems that the click event is triggered "very" late.
Feel free to test it here:
https://jsfiddle.net/hcetz1og/2
Question:
How long will it take until the click event arrives at the document? Is there a fixed time span I have to wait or can the delay depend on every machine?
If so, I am forced to not use plain CSS but must use javascript for this I think.
Edit:
Feel free to post an alternative solution using plain css. But please be aware that I mainly want to focus on getting an answer to this question, not alternative solutions.
As #Mark Baijens said in the comments, using timeouts is a bad practice, so here is a pretty clean solution.
I used JavaScript to render the dropdown, not the CSS, because the CSS is where Your issue is coming from.
I don't know why would You want to set the innerHTML, but not some other property, like style.visibility for example. It just doesn't make sense to me, so with that in mind, let's get our hands on this :)
Working demo >> HERE <<.
Step 1 - remove the #outer...:hover parts of CSS
So, You are left with this:
#outer {
width: 200px;
position: relative;
}
#input {
width: 100%;
}
#results {
position: absolute;
width: 100%;
display: block;
visibility: hidden;
background-color: white;
}
#results > div:hover {
background-color: lightblue;
cursor: pointer;
}
Step 2 - add the onfocus event to the input field
Just assign a function call to the onfocus attribute of the input. Everything else in the HTML stays the same.
<div id="outer">
<input type="text" id="input" onfocus="showElements()">
<div id="results">
<div>Test 1 </div>
<div>Test 2 </div>
<div>Test 3 </div>
<div>Test 4 </div>
</div>
</div>
<div id="label">
</div>
Step 3 - create the showElements and hideElements function:
function showElements() {
document.getElementById("results").style.visibility = 'visible';
}
function hideElements() {
document.getElementById("results").style.visibility = 'hidden';
}
Step 4 - call the hideElements() when clicked outside the input element
There are two cases for the click outside the input element:
Case 1 - we clicked on one of the divs inside the #results wrapper
Case 2 - clicking outside the input field, but not on one of the divs inside the #results wrapper
In the first case, we will modify the assignment of the onclick handler like this:
document.querySelectorAll("#results div").forEach(setClick);
function setClick(node) {
node.addEventListener("click", setTextAndHideElements.bind(null, node.innerHTML));
}
So, the setText function now becomes setTextAndHideElements and looks like this:
function setTextAndHideElements(t) {
document.getElementById("label").innerHTML = t;
hideElements();
}
For the second case (clicking outside the input field, but not on one of the divs inside the #results wrapper), we must watch for the click on the whole page (document element), and respond to the action like this:
document.onclick = function(e) {
if (e.target.id !== 'input'){
hideElements();
}
}
Note: this will override any previously assigned onclick events assigned to the document element.
As mentioned in the beginning, working demo is >> HERE (codepen.io) <<.
I tried another solution which requires no setting of additional JS events.
See: https://jsfiddle.net/hcetz1og/4/
I gave every result item a tabindex of "0" to ensure, those items can be focusable.
Then i removed the #outer:not() part from the css and replaced the hover selector with this: #results:focus-within. Additional I called node.blur() on the node after clicking onto them.
Summary:
Change in HTML:
<div tabindex="0">Test 1 </div>
Change in JS:
function setText(t, node) {
document.getElementById("label").innerHTML = t;
node.blur();
}
Change in CSS:
#outer:focus-within #results, #results:focus-within {
visibility: visible;
}
What do you think about this one? Should be stable I think because the focus onto the #results div is set before the click event is triggered onto the result item.
Event order should be (based on my observation):
input focus -> input blur -> item focus -> item click
Not sure if the step between blur and focus can lead to a visible problem. Theoretically, the results div must be hidden and shown again in a very small amount of time.
But I investigated this with chrome's performance timeline and did not recognize a new render between both events. One can see, that the result item is focused (outline is set onto it) and then it disappears as expected.

mouseout closes the trigger and not the target

I have set a popup to open at mouse over on an element. The popup should close when the cursor is off it.
For some reason the popup window closes when the cursor is right off its opener button, instead.
You can see an example here:
http://www.friends.wwz.co.il/Lab/Gefen/Generali/es/popup.html
Please try to hover with the mouse on the "lee mas" button. A popup will open. It should close at hovering off it. But instead it closes at hovering off the lee mas button, so it closes immediately.
Any idea where do I go wrong?
Many thanks in advance for you advice
The main problem is you are attaching hover events to the button. Once you hover out of the button element, it fires the hoverOut button.
So the ideal course of action can be:
Bind hover event to the button.
Once hovered, a pop-up is dynamically added.
Then, a hover event is bounded to the pop.
And the code of dissolving the pop-up is attached in the hover-out function.
So that when actually the cursor is hovered out of the pop-up it dissolves.
Apart from that, Just have a look at this fiddle. It has two hyperlinks for hover. The first is the one you are facing. The second one is the one you are looking for. :D
Code for it:
$(document).ready(function() {
$("#toggleSwitch_j").hover(
function() {
$("#theBox_3").slideDown(500);
}, function() {
$("#theBox_3").slideUp(500);
});
$("#StayOpen").hover(
function() {
$("#theBox_2").slideDown(500);
}, function() {
$("#theBox_2").slideUp(500);
});
});
body {
background-color: #eef;
}
#theBox_3,
#theBox_2 {
display: none;
border: 1px solid #000;
width: 200px;
height: 100px;
background-color: #ddf;
}
#toggleSwitch_j,
#StayOpen {
background-color: #cacaca;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
This layout will only keep the hidden div visible as long as you point to the link
<br>You'll never be able to reach anything inside the div
<br>jQuery Hover
<div id="theBox_3">Peek-a-boo!</div>
<hr>This layout puts the link and hidden div inside a wrapper - hovering anywhere inside the wrapper expands the hidden div, so you can reach content inside it. This would be handy if you need to put links or form elements inside the hidden div, instead of
just text to read.
<div id="StayOpen">
jQuery Hover
<div id="theBox_2">Peek-a-boo!</div>
</div>

Change the width of a div when clicked

I have no idea what goes wrong with my code but it gives me errors every time I click the blue Chat Here button. The button's supposed to be shorter when the iframe is hidden then when the iframe slides up, the blue button should take up the whole space the same as the iframe as well.
View Demo
Here's the JS I have so far
function showChat() {
jQuery("#blk-collaboration .chatbox").slideToggle("slow",function(){
jQuery("#blk-collaboration #toggle").css("background","#45A1F1");
});
jQuery('#btn-chat').click(function() {
$(this)//this is a button DOM element. wrapping in jQuery object.
.toggleClass('wide'); // removing wide class so button became smaller.
});
}
$(document).ready(function(){
$('.chatbox').hide();
});
I'd greatly appreciate if you could provide me a demo as well. Been working on this code for two days now and I haven't found the right solution yet.
First of all, you forget to put ; on your height property. Another important thing is that you need to change your class position like this:
.button {
background: #45A1F1;
height: 40px;
width: 620px; /*Width of regular button */
}
.wide {
background: #45A1F1;
height: 40px;
width: 300px; !important; /* Width of wide button */
}
You can simplify your code by using this jQuery:
$(document).ready(function(){
$('.chatbox').hide();
$('#btn-chat').click(function() {
$("#blk-collaboration .chatbox").slideToggle("slow",function(){
$("#blk-collaboration #toggle").css("background","#45A1F1");
});
$(this)//this is a button DOM element. wrapping in jQuery object.
.toggleClass('wide'); // removing wide class so button became smaller.
});
});
Check out this Fiddle..

How to scroll to the needed div in wrapper?

There is the following code:
<div id="main">
<div id="first">
First item
</div>
<div id="second">
Second item
</div>
<div id="third">
Third item
</div>
</div>
CSS styles:
#first {
background-color: yellow;
}
#second {
background-color: blue;
}
#third {
background-color: green;
}
html, body, #main, #first, #second, #third {
height: 100%;
}
There are 3 div in main div and user scrolls in order to go to the bottom of the screen. I want to do the following thing: when user scrolls with mousewheel the first time he goes to "second" div, the second time - "third" div. This feature has been realized on the following site: some site. Please, tell me some advice. Thanks.
Living demo: http://jsfiddle.net/xz2DN/1/
Update for IE: http://jsfiddle.net/xz2DN/6/
(html element is scrolled, as the body contains the css property overflow:hidden which makes it impossible to scroll for IE)
You need to:
Disable the scrolling bar on the site
Detect the mousewheel event (not working in old browsers)
Have knowledge of which section are you in before scrolling up or down
Update that status once you move from one to another section
You could try to look for each of this points by separate and you will get it working :)
Disabling scrolling bar and default scrolling
body,html{
overflow:hidden;
}
Detecting mouse wheel:
http://www.sitepoint.com/html5-javascript-mouse-wheel/
Having knowledge of which section are we in
I would recommend you to create an array with a pair of values id, status. The id would be the id tag of the current section and the status would indicate you if is the current "slide"/section or not.
You could do something like this:
var sections = {};
var numberSections = 0;
$('.section').each(function(index, element){
//current slide is active
if(!index){
sections[$(this).attr('id')] = 1;
}
else{
sections[$(this).attr('id')] = 0;
}
numberSections++;
});
And then you should define two functions, one to move update the status of the array when the user scrolls up, and the other for the case in which they scroll down.

HTML button does not click even when is focused and the CSS rules kick in

Take a look at this button: http://jsfiddle.net/vtortola/Dnxpe/
I Chrome, if you click on the top border, even when the ":hover" and ":active" css rules triggers, the event is not triggered. If you click more in the center, then it works fine.
In IE9, if you do the same it miss the 50% of the clicks.
The problem are the margins, when you click the margins switch, giving the effect of the button being pushed, but this makes the pointer be out of the button if you are clicking the top border but... the event should be already triggered.... why this happen?
Thanks.
This probably happens because a click() is considered complete only once both mousedown() and mouseup() are completed in succession. In the case of clicking near the top border mouseup() never gets triggered and the same holds true for click().
If you use mousedown() it'll work every time, but it'll happen before the entire click is completed.
$('button').mousedown(function(e){
$('#clicks').append('<span>click </span>');
});
To solve this you could do the following:
Add a container to the button and then add a mousedown handler to the button and a mouseup handler to the container. If you make sure they were both invoked, then you can be sure that a click event on the button has been performed.
Like so:
HTML
<div id="cont"><button>Click me</button></div>
<div id="clicks">
</div>​​​​​​​​
JavaScript
var mdown = false;
$('button').mousedown(function(e){
mdown = true;
});
$('#cont').mouseup(function(e){
if (mdown)
{
$('#clicks').append('<span>click </span>');
mdown = false;
}
});
​
I checked out you code and find an interesting bug.Its because of CSS.
Even you can regenerate that bug.
Steps:
1. Click on button near top border but do not release your finger from mouse.
2. Now, look at the button,your button is actually pushed down and that's why,it does not able to fire button click event.
Solution:
Remove margin when button is active.
button:active,input[type=submit]:active
{
box-shadow: 0px 0px 3px 3px #D8F6CE;
}
I hope it helps.
Here's the solution I came up with that uses a css psudo element to capture the click event. The benefit here is that you don't need javascript. The click events are always captured by the :after and bubble up to the button.
http://jsfiddle.net/kevinrockwood/fUuUB/1/
The main thing to note is the css:
button:after{
content: " ";
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
}

Categories

Resources