I have a button which opens a modal. This button should always be visible. If the user scrolls down the page, the button should be "affixed" on top.
I use bootstrap v3.1.1 and the affix.js is loaded in the head and is attaching the "affix-top" class to my button like it should.
But when I scroll - I have tried several variants of implmentation - the affix-top class is not changing to affix.
Here is my code.
HTML:
<button class="btn btn-primary m-t-10 m-b-10"
id="standard-questions-button"
type="button"
data-toggle="modal"
data-target="#standard-questions-modal">
Show Questions
</button>
JS:
$('#standard-questions-button').affix({
offset: {top: $('#standard-questions-button').position().top}
});
// for testing only
$('#standard-questions-button').on('affix.bs.affix', function () {
alert('affix');
})
CSS:
#standard-questions-button.affix-top {
color: red;
position: static;
margin-top:25px;
width:228px;
}
#standard-questions-button.affix {
color: green;
top:25px;
float: right;
width:228px;
}
Approach 2 (also not working: wrapped in a function)
$('#standard-questions-button').affix({
offset: {top: function(){return $('#standard-questions-button').position().top}}
});
How can I solve this?
Do I miss something?
I had exactly the same issue. Maybe you have the similar problem.
In bootstrap script (I use the version 2.3.2) there is a code fragment that assigns event handler to the window's scroll:
this.$window = $(window)
.on('scroll.affix.data-api', $.proxy(this.checkPosition, this))
I realized that in my code the scroll event wasn't fired at all so the class was not being changed. When I changed only this fragment for:
this.$window = $('#main_container')
.on('scroll.affix.data-api', $.proxy(this.checkPosition, this))
It finally started to work. Just check what is the element you are scrolling on your webpage and set it in bootstrap's Affix function definition.
You may find helpful this to check if you are able to scroll through window:
$( window ).scroll(function() {
alert("scroll");
});
Related
I'm using Bootstrap's Buttons plugin toggle state feature on a custom checkbox. When this button is toggled (on), a hidden element should appear near the top of the page and then the page should scroll to the top. When the button is toggled next (off), the element should disappear and the page should scroll to the top.
The hidden element is toggled when the button is toggled on and off but the scroll is not working.
Changing the data-toggle from 'buttons' to 'button' makes the scroll work but does not visibly toggle the button so that is no good.
I tried setting an onchange event for the checkbox itself but that also doesn't scroll.
It seems that Bootstrap's function for the onclick event is doing something that doesn't allow my onclick function to run properly. I failed at trying to understand it so far (I will keep trying).
Setting a timeout for the window.scrollTo() function makes it work. Why could this be? Is there a way to do this without the timeout?
<body>
<div class="container">
<div>
<h3>Toggle Header and Scroll To Top</h3><hr />
<h1 id="displayMe" class="d-none">Display Me</h1>
<div style="height: 100vh;"></div>
<div class="btn-group-toggle btn btn-success custom-control custom-switch" data-toggle="buttons" id="myButton">
<input type="checkbox" class="custom-control-input" id="myCheckbox">
<label class="custom-control-label" for="myCheckbox">Toggle and Scroll</label>
</div>
</div>
</div>
<script src="assets/jquery/jquery.min.js"></script>
<script src="assets/twitter-bootstrap/js/bootstrap.min.js"></script>
<script>
$(document).ready(function () {
$("#myButton").on("click", toggleAndScroll);
});
function toggleAndScroll() {
$('#displayMe').toggleClass('d-none');
//setTimeout(function () {
window.scrollTo(0, 0);
//}, 100);
}
</script>
</body>
https://jsfiddle.net/ews9q50v/
(Uncomment the setTimeout lines to see it working)
I have no idea why it doesn't work. Bootstrap is messing something up.
But you can easily bypass it by moving scrollTo to the end of event loop using setTimeout(func, 0).
$("#myButton").on("click", toggleAndScroll);
function toggleAndScroll() {
$('#displayMe').toggleClass('d-none');
setTimeout(function () {
window.scrollTo(0, 0);
}, 0);
}
demo
This seems to work for chrome but Firefox is having other issues:
function toggleAndScroll() {
$('#displayMe').toggleClass('d-none');
window.scrollTo({
top: 0,
left: 0,
behavior: 'smooth'
});
}
But since you are using jQuery something like this works in both Firefox and chrome:
function toggleAndScroll() {
$('#displayMe').toggleClass('d-none');
$("html, body").animate({ scrollTop: 0 }, "slow");
}
I'm not entirely sure what the issue is - probably something to do with the scroll height changing at the same time as scrollTo is called.
I have two divs with dynamic links that sometimes are populated and sometimes aren't. If you a click a link that looks like this:
<a href="#">
Nothing happens, I prevent the default action, but if the link is:
<a href="/path/to/a/page">
It will follow.
I want to able to click on the surrounding div, and using the same logic as above. So if I click on the red and there is a valid link the link will follow. I'm using trigger() to attempt this.
The situation is below:
$(function() {
$(".container").click(function(e) {
var clickTarget = $(e.target).attr("href");
var clickTargetLink = $(this).find(".container-link");
if ((clickTarget != "#") || (clickTarget != undefined)) {
clickTargetLink.trigger("click");
} else {
e.preventDefault();
}
});
});
.container {
padding: 50px;
margin-bottom: 1rem;
background-color: red;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="container">
<a class="container-link" href="google.com">link</a>
</div>
<div class="container">
<a class="container-link" href="#">link</a>
</div>
What ends up happening is clicking on the red div with google.com in the link throws:
jquery.js:3988 Uncaught RangeError: Maximum call stack size exceeded
at String.replace ()
at camelCase (jquery.js:3988)
at Data.get (jquery.js:4069)
at HTMLDivElement.dispatch (jquery.js:5146)
at HTMLDivElement.elemData.handle (jquery.js:4991)
at Object.trigger (jquery.js:8249)
at HTMLAnchorElement. (jquery.js:8327)
at Function.each (jquery.js:354)
at jQuery.fn.init.each (jquery.js:189)
at jQuery.fn.init.trigger (jquery.js:8326)
Maximum call stack size exceeded error
- this suggests that there is an infinity loop somewhere in this code. I'm lost as to how that could be the case here?
Why is this code causing a maximum call stack size error?
EDIT: more research led me to: Maximum call stack size exceeded on trigger click
going
.triggerHandler() just renders nothing and the link doesn't follow. Is there a different action I need to use that I'm not aware of?
EDIT #2: a few more details:
I cannot change/add markup structure.
the red div needs to be fully clickable, as there is another action tied to it when the link is empty: <a href="#">
I tried e.stopPropagation(); - which resolves the error, but then the link doesn't follow
I tried triggerHandler() - http://api.jquery.com/triggerhandler/
Your click handler is triggering a new click event, which is handled by the click handler, which triggers a new click event, which is handled by the click handler, which... eventually overflows the stack.
In comments (and now in the updated question) you've clarified the purpose of this code:
The red area needs to be clickable, not just the link. That's why I'm trying to use .trigger() so that I can trigger a click in the child link inside the div when the div is clicked on.
This necessitates a few more changes beyond just fixing your infinite loop:
Your existing code was looking for the href on the event target itself, which would not have allowed the red area to be clickable (because the event target isn't necessarily the element with an href attribute). I've changed this to ignore the event target (which isn't really what you care about) and instead just pluck the href from the link inside the container element.
In your if clause you used || when you meant && ("if it's not X or it's not Y" will always be true; "if it's not X and it's not Y" is what you meant there.)
The infinite recursion is resolved here by simply setting the window location directly, rather than firing another click event.
$(function() {
$(".container").click(function(e) {
var clickTarget = $(this).find("a.container-link").attr("href");
if (clickTarget && clickTarget !== "#") {
window.location=clickTarget;
} else {
// prevent link navigation from occurring
console.log("Blocking click");
e.preventDefault();
}
});
});
.container {
padding: 10px;
margin-bottom: 1rem;
background-color: red;
cursor: pointer
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="container">
<a class="container-link" href="https://example.com">Normal link</a>
</div>
<div class="container">
<a class="container-link" href="#">Blocked link</a>
</div>
You could optionally check to see if it is the link or the container that was clicked. If it was the link, do not trigger another click.
$(function() {
$(".container").click(function(e) {
var $containerLink = $(this).find('.container-link');
//check to see if the container was clicked, or if the link was clicked
var linkWasClicked = $(e.target).is('.container-link');
if (['#', undefined].indexOf($containerLink.attr('href')) > -1) {
e.preventDefault();
} else if (!linkWasClicked) { //only trigger the link if it was not clicked
$containerLink[0].click();
}
});
});
.container {
padding: 50px;
margin-bottom: 1rem;
background-color: red;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="container">
<a class="container-link" href="https://google.com">link</a>
</div>
<div class="container">
<a class="container-link" href="#">link</a>
</div>
When the container is clicked, you trigger a click on the link inside it which propagates back to the container, clicking on the link again, and so on. You are recursively clicking on the link over and over again, which is causing the error. In your click event handler for the container, you need to check if the event's currentTarget is equal to the event's target to click on the link (preventing recursion). To trigger the click on the link to redirect to another page, you will need to get the actual DOM element to click on (not the jQuery object) like this: clickTargetLink[0].click() (jQuery always returns an Array of elements).
.container {
padding: 50px;
margin-bottom: 1rem;
background-color: red;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="container">
<a class="container-link" href="http://www.example.com">link</a>
</div>
<div class="container">
<a class="container-link" href="#">link</a>
</div>
<script>
$(function() {
$(".container").click(function(e) {
var clickTarget = $(e.target).attr("href");
var clickTargetLink = $(this).find(".container-link");
if (e.currentTarget==e.target) {
clickTargetLink[0].click();
} else {
if(clickTarget!==undefined&&clickTarget == "#"){
e.preventDefault();
}
}
});
});
</script>
Because .container-link is inside .container the click event of the .container-link triggers a click also for .container unless you do stopPrpogation(), what causes the infinite loop.
Use stopPropagation() inside the if (after triggering the click).
as you can see in comments you are triggering click in the handler for the click which is causing to go in a loop . click-> handler-> trigger click-> handler --->
so basically attach handler to link itself, or if you want to attach it to parent only check if the target is link
you can also look at this thread for more breadth on topic In jQuery, how can I tell between a programmatic and user click?
Just improve your JS this way:
$(function () {
$(".container").click(function (e) {
var clickTarget = $(e.target).attr("href");
var clickTargetLink = $(this).find(".container-link");
if ((clickTarget === "#") || (clickTarget === undefined)) return false
})
})
Well, i am stucked and can't find the answer myself. Hopefully someone can give me a hint.
I try to fullfill the following requirements:
There should be a Newsblock within a HTML Page with a fixed width and
height.
In this Newsblock only the title of the news are visible.
Those news are "collapsed" by default and should "expand" if the Mouse is over it.
Due the fact that the 'Newsblock' is limited by its height, there should be a Scrollbar visible. But only if the currently expanded news makes it necessary, so the user can Scroll down.
Newstitle and Newstext should never leave the Newsblock.
so far so good, i was able to fullfill all those demands except the one with the Scrollbar. If i try to reach the Scrollbar out of the currently expanded news it collapses again and the Scrollbar disappears. I understand that my .hover is configured that it always SlideUp if i leave the newsentry and the Scrollbar isn't a part of the newsentry div. But i have no idea what to change to still have an overall Scrollbar for the Newsblock, but won't disappear if i try to 'reach' it.
P.s.: A Scrollbar only per Newsentry looks weird. Thats why i want 'bind' the scrollbar to the parent container :S
HTML
<div id="newsblock">
<div> // some auto generated div's i have to life with, so the news entries are not 'direct' children of the newsblock.
<div class="newsentry">
<div class="newstitle">...</div>
<div class="newstext">...</div>
</div>
... another 9 'newsentry' divs.
</div>
</div>
JS
$(".newsentry").hover(
function() {
$(this).children(".newstext").stop(true,true).slideDown();
},
function() {
$(this).children(".newstext").stop(true,true).slideUp();
}
);
CSS
.newsblock {
height: 200px;
overflow-y: auto;
}
Instead of closing a .newsentry when the cursor goes out of it, a solution can be to close it only when it enters another .newsentry or when it leaves #newsblock.
The scrollbar being part of #newsblock, the entry isn't closed anymore when you go on it.
EDIT: Following our discussion about the scroll issue, I added a step callback to the closing animation to make sure that the top of the .newsentry getting opened remains visible when the other entries are getting closed.
Here is a working example:
var $newsblock = $("#newsblock");
function closeAllNews(slideUpArgs){
return $(".newstext").stop(true).slideUp(slideUpArgs);
}
function openNews(news, slideDownArgs){
$(news).find(".newstext").stop(true).slideDown(slideDownArgs);
}
function ensureNewsTopVisible(news){
// Check if the top of the newsentry is visible...
var top = $(news).position().top;
if(top < 0){
// ...and if not, scroll newsblock accordingly.
$newsblock.scrollTop($newsblock.scrollTop() + top);
}
}
$(".newsentry").each(function(){
var $this = $(this);
// When the mouse enter a news entry...
$this.on("mouseenter", function(){
// ...close all opened entries (normally there is at most one)...
closeAllNews({
// (while making sure that the top of this entry remains visible
// at each step)
step: ensureNewsTopVisible.bind(null, $this)
});
// ...open this newsentry.
openNews($this);
});
});
// When the mouse get out of the newsblock, close all news.
$newsblock.on("mouseleave", closeAllNews);
.newstitle {
font-size: 2em;
}
.newstext {
display: none;
}
#newsblock {
max-height: 150px;
overflow: scroll;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="newsblock">
<div>
<div class="newsentry">
<div class="newstitle">News 1</div>
<div class="newstext"></div>
</div>
<div class="newsentry">
<div class="newstitle">News 2</div>
<div class="newstext"></div>
</div>
<div class="newsentry">
<div class="newstitle">News 3</div>
<div class="newstext"></div>
</div>
<!-- Etc. -->
</div>
</div>
<!-- Ignore the script below. It is just filling in the news' text. -->
<script>
$(".newstext").each(function(i, newstext){
$.get("http://baconipsum.com/api/?type=meat-and-filler&format=html¶s=5&num=" + i)
.then(function(ipsumHtml){
$(newstext).html(ipsumHtml);
});
});
</script>
Try this:
$(".newsentry, .newsblock").hover( // <-- changed
function() {
$(this).children(".newstext").stop(true,true).slideDown();
},
function() {
$(this).children(".newstext").stop(true,true).slideUp();
}
);
This makes sure the block stays open when you hover either over the header or the block itself.
Is that what you mean?
There would be a joke , if i am wrong .. what i thing just change your css as
/* not .newsblock **/
#newsblock {
height: 200px;
overflow-y: scroll;/* not auto*/
}
It will be a lot better if you use click operation instead of hover to slide down news text block because the user can accidentally hover over any of the news entry in order to reach for the scroll bar. I think you need a accordion like functionality. You can use the below code if you are fine with click instead of hover.
$(".newsentry").click(
function() {
$(".newstext").stop(true,true).slideUp();
$(this).children(".newstext").stop(true,true).slideDown();
}
);
Or use the below one to go with hover.
$(".newsentry").hover(
function() {
$(".newstext").stop(true,true).slideUp();
$(this).children(".newstext").stop(true,true).slideDown();
},
function(){}
);
This will not close the news text block until you accidentally hover over another news entry.
I have a trigger element and a responding element.
<div class="more"></div>
<div class="info"></div>
I would like to bind an open/close type event.
$('.more').delegate($('.more'), 'click', function(){
$(this).removeClass('more');
$(this).addClass('less');
$(this).text("less...");
$('.info').addClass("open");
});
$('.less').delegate($('.less'), 'click', function(){
$(this).addClass('more');
$(this).removeClass('less');
$(this).text("more...");
$('.info').removeClass("open");
});
It doesn't work as intended, if the second function is nested in the first then you can open and close only once.
If the script is formatted sensibly as above it will open but not close.
Could anyone help me out?
Bonus if the script could support the .info could be either a sibling or the element immediately following $(.more/.less)'s parent.
I've been toying with .on/.live/.bind but less successfully than above.
Use event delegation ,and binded to document or immediate parent,not same element
$(document).on( 'click',".more", function(){
$(this).removeClass('more');
$(this).addClass('less');
$(this).text("less...");
$('.info').addClass("open");
});
$(document).on('click',".less", function(){
$(this).addClass('more');
$(this).removeClass('less');
$(this).text("more...");
$('.info').removeClass("open");
});
DEMO
NOTE: delegate was outdated with latest version of jquery ,so use on instead,
ISSUE: you are delegated with same element $('.less'),$('.more') use immediate parent or document
Just use JavaScript to toggle a class, and let CSS magic do the rest. Here is a demo: http://jsfiddle.net/pomeh/69sX5/1/
And here is the code:
HTML
<div>
Some visible content
</div>
<div class="content-fold">
<div class="more">More...</div>
<div class="less">Less...</div>
</div>
<div class="info">Some hidden additional content</div>
CSS
/* Additional content and Less button hidden by default */
.content-fold + .info, .content-fold .less {
display: none;
}
/* Additional content and Less button shown when class shown is active */
.content-fold.shown + .info, .content-fold.shown .less {
display: block;
}
/* More button hidden when additional content is shown */
.content-fold.shown .more {
display: none;
}
/*
You can also move the "div.info" into the "div.content-fold",
and use ".content-fold.shown > .info" instead of ".content-fold.shown + .info"
Browser support is quite good for adjacent selector (see http://www.quirksmode.org/css/selectors/#t11 and https://developer.mozilla.org/en-US/docs/Web/CSS/Adjacent_sibling_selectors#Browser_compatibility)
*/
JavaScript
$('.content-fold').on('click', function(){
$(this).toggleClass('shown');
});
Use id to do your task. it's easy.
Html
<div class="more" id="toggle"></div>
<div class="info"></div>
Jquery
$('#toggle').click(function(){
var $this = $(this) //store object
if($this.hasClass('more')) {
$this.removeClass('more').addClass('less').text('Less...')
$this.next('.info').addClass('open');
} else {
$this.removeClass('less').addClass('more').text('More...')
$this.next('.info').removeClass('open');
}
});
js Fiddle: http://jsfiddle.net/5N6TL/53/
I am making a personal website. I want to make it so that cliking the background changes the theme from dark to light and vice/versa. It works, but I dont want it to switch the theme if the user clicks on text, only the background of the webpage For example, if I click the text at the bottom it changes the css, but it should only do that if you click the white background.
Here is my code (Mainly checkout js/main.js, the switchTheme function and the index.html) and the website itself.
You are targeting your container class. Anytime that div (or anything in it) gets clicked, that event will fire. Try stopping the event propagation on your click event if $('this').selector === 'p' or whatever class you're using.
Also - not bad for 13 boss!
$( document ).click(function( event ) {
// if statement here
event.stopPropagation();
// else the regular behavior
});
Thanks everyone for your help! I'd almost given up and wanted to use a button to toggle it instead. The more you know!
And since this is an answer to my question: e.stopPropogation()
You can also do something like this:
document.querySelector('div').addEventListener('click', (e) => console.log("Heeeeyyy! Hoooo!"))
<div style="position: fixed; background-color: lightcoral; width: 500px; height: 180px;">
</div>
<div style="position: fixed; background-color: lightblue; width: 300px; height: 100px;">
<p>You can not click through me!</p>
</div>
Don't put the elements inside the "parent".
Move them together only by style.
How about
$("#container").on("click", "div", function(event){
event.stopPropagation();
switchTheme();
});
Add a test to see if the ID of the clicked element was actually the container.
function switchTheme( event ) {
if ( event.target.id === 'container' ) { //the container was clicked, and not a text node
if (dark) {
$("#container").css("background-color", "rgba(255,255,255,0.7);");
$("#container").css("color", "black");
dark = false;
} else {
$("#container").css("background-color", "rgba(0,0,0,0.7);");
$("#container").css("color", "white");
dark = true;
}
}
}
Try setting the following:
$("#container").on("click", "div", function(e){
e.stopPropagation();
});