Bootstrap 3 expand accordion from URL - javascript

Using Bootstrap 3, I'm trying to use sub-navigation anchor links (ie, index.php#wnsh) to expand a specified accordion and anchor down the page to the content. I've tried searching for examples but with little luck, likely because my accordion structure is different from the given BS3 example. Here's my HTML:
UPDATE:
Made a few updates to the code, but it still isn't opening the accordion specified by the hash. Any further thoughts?
<div id="accordion" class="accordion-group">
<div class="panel">
<h4 id="cs" class="accordion-title"><a data-toggle="collapse" data-parent="#accordion" data-target="#cs_c">Child Survival: Boosting Immunity and Managing Diarrhoea</a></h4>
<div id="cs_c" class="accordion-collapse collapse in">
<p>...</p>
</div>
<h4 id="chgd" class="accordion-title"><a data-toggle="collapse" data-parent="#accordion" data-target="#chgd_c">Child Health, Growth and Development: Preventing Mental Impairment with Iodine and Iron</a></h4>
<div id="chgd_c" class="accordion-collapse collapse">
<p>...</p>
</div>
<h4 id="wmnh" class="accordion-title"><a data-toggle="collapse" data-parent="#accordion" data-target="#wmnh_c">Women’s and Newborn Survival and Health: Iron Supplementation and Food Fortification</a></h4>
<div id="wmnh_c" class="accordion-collapse collapse">
<p>...</p>
</div>
</div>
</div>
JS
var elementIdToScroll = window.location.hash;
if(window.location.hash != ''){
$("#accordion .in").removeClass("in");
$(elementIdToScroll).addClass("in");
$('html,body').animate({scrollTop: $(elementIdToScroll).offset().top},'slow');
}
Thanks in advance. Any help would be appreciated.

Tested and working in Bootstrap 3.3.5.
<script type="text/javascript">
$(document).ready(function () {
if(location.hash != null && location.hash != ""){
$('.collapse').removeClass('in');
$(location.hash + '.collapse').collapse('show');
}
});
</script>

I encountered the same problem just few minutes ago. The solution appears to be straight forward - you need to parse an URL and add class in to the matchable accordion, using its id:
// Opening accordion based on URL
var url = document.location.toString();
if ( url.match('#') ) {
$('#'+url.split('#')[1]).addClass('in');
}
Tested and working in Bootstrap 3.1.1.

With Bootstrap 4.4, just adding this single line to my JS code (inside a document ready clause) seems to do the trick:
if(location.hash != null && location.hash != ""){$(location.hash + '.collapse').collapse('show');}

I'm using this in Yii2 with the Collapse widget.
Assign an id to your panels.
If you have plain html you can just add an id to your a-tag and update the selector.
$(function(){
var hash = document.location.hash;
if (hash) {
$(hash).find('a').click();
}
});

Just a response, to Ilia R's. His solution worked great! The only thing, though, was that the panel title style wasn't updating (the .collapsed class needed to be removed from the panel title link), so I tweaked Ilia R's code a tad. Someone probably has a better / cleaner solution, but here's a start.
$(document).ready(function() {
var url = document.location.toString();
if ( url.match('#') ) {
$('#'+url.split('#')[1]).addClass('in');
var cPanelBody = $('#'+url.split('#')[1]);
var cPanelHeading = cPanelBody.prev();
cPanelHeading.find( ".panel-title a" ).removeClass('collapsed');
}
});

For me worked:
$(document).ready(function() {
var url = document.location.toString();
if ( url.match('#') ) {
var cPanelBody = $('#'+url.split('#')[1]);
cPanelBody.find(".panel-title a")[0].click();
}
});

Related

Bootstrap accordion jumps when expanding

I am using a snippet to load external divs to Bootstrap accordion items.
Initially, the divs were loaded on page load, but then I modified the code so that they would load on button click.
I have used two different snippets to make this happen, and they both result in a rather jumpy transition of the accordion items when expanding. However, this didn't happen when the divs where loaded on page load.
I have searched for a solution to this problem, I have tried many things that worked for other people (such as zero margin and padding or enclosing both the button and the panel-body inside a div), but none of them worked in this case.
If you have any suggestions, please tell me.
Thank you in advance
HTML
<div class="panel-group" id="accordion" role="tablist" aria-multiselectable="true">
<div class="panel panel-default">
<div class="panel-heading" role="tab" id="headingOne">
<h4 class="panel-title">
<a data-href="/path/to.html #firstdiv" class="ajax-link" data-ajaxtarget="#first" role="button" data-toggle="collapse" data-parent="#accordion" href="#collapseOne" aria-expanded="true" aria-controls="collapseOne">
Read more
</a>
</h4>
</div>
<div id="collapseOne" class="panel-collapse collapse" role="tabpanel" aria-labelledby="headingOne">
<div class="panel-body" id="first"></div>
</div>
</div>
</div>
JS/JQuery
(function($){
$(document).ready(function(){
ajaxLink();
linkPreventDefault();
});
function ajaxLink(){
$("a.ajax-link").on('click', function(event){
var thisLink = $(this);
var targetUrl = thisLink.attr("data-href");
var target = thisLink.data('ajaxtarget');
console.log("loading via ajax: "+ targetUrl + ",in div: "+ target);
$(target).load( targetUrl, function( response, status, xhr ) {
if ( status == "error" ) {
var msg = "Sorry but there was an error: ";
console.error (msg + xhr.status + " " + xhr.statusText );
}else if(status="success"){
console.log("successfully loaded");
//OPTIONAL: unbind the links click event and bind a new one
noMoreAjax(thisLink);
}
});
event.preventDefault();
});
}
function linkPreventDefault(){
$('a.link-prevent').click(function(event){
console.log
event.preventDefault();
});
}
function noMoreAjax(item){
var linkItem = $(item);
console.log("No more Ajax for this link");
linkItem.removeClass('ajax-link');
linkItem.addClass('link-prevent');
linkItem.unbind('click');
$(linkItem).click(function(event){
console.log("Preventing after ajax");
event.preventDefault();
});
}
})(jQuery);
(OLD JS:
$(document).ready(function(){
$('#accordion').click(function(){
$('#first').load('/path/to.html #firstdiv');
});
})
)
You should expand accordion item once received response for the ajax call. So remove data-toggle="collapse" href="#collapseOne" attributes from your link and call $('#collapseOne').collapse() in callback function, like this:
$(target).load( targetUrl, function( response, status, xhr ) {
if ( status == "error" ) {
var msg = "Sorry but there was an error: ";
console.error (msg + xhr.status + " " + xhr.statusText );
}else if(status="success"){
$('#collapseOne').collapse();
console.log("successfully loaded");
//OPTIONAL: unbind the links click event and bind a new one
noMoreAjax(thisLink);
}
});
See a working sample here
I've used setTimeout() to fake ajax call, so you'll need to update that part, but other than that, everything is there.

template tag does not include content

I have some html wrapped in a template, however when I try to activate the template and change some of the elements within the template, it comes back as null. I have done a console.log on the template and all that comes back is the start of the template tag and end tag e.g. <template id="panel-template"></template> this is the rendered version, and as you can see none of the html that is supposed to be encapsulated within are not present.
Here is my template
<template id="panel-template">
<div class="panel panel-default" draggable="true">
<div class="panel-heading">
<h3 class="panel-title">
Panel title
<span class="glyphicon glyphicon-pencil panel-icons"></span>
<span class="glyphicon glyphicon-zoom-in panel-icons"></span>
<span class="glyphicon glyphicon-trash panel-icons"></span>
</h3>
</div>
<div class="panel-body">
</div>
</div>
and here is part of the javascript that is related to the template, I have of course wrapped this in document.ready
if ('content' in document.createElement('template')) {
//var widgetCount = document.getElementById('columns').getAttribute('data-count');
var widgetModel = #Html.Raw(Json.Encode(Model.widgets));
for (var i = 0; i < widgetModel.length; i++) {
var clone = loadwidgets(widgetModel[i]); //This function is in an external js file
var inputDestination = document.querySelector('#col2')
console.log(inputDestination);
inputDestination.appendChild(clone);
}
}
and here is the loadWidgets function.
function loadwidgets (jsonObject) {
var template = document.querySelector('#panel-template');
var panelTitle = template.querySelector('div.panel-title');
var widgetType = template.querySelector('div.panel-body');
console.log(template);
//We give the widget a name on top of panel, this will be user defined.
panelTitle.textContent = jsonObject.WidgetName;
widgetType.setAttribute('id', jsonObject.WidgetCode);
return document.importNode(template, true);
}
I get an error saying panelTitle is null, but I think this is because when activating the template, it doest not seem to pull through the encapsulated html elements. I am unsure how to proceed.
I see in your Javascript part that you state that if you create an element. You need to start the code. But i don't see you craeting the element "template"
You should use querySelector() on the template's content property:
var panelTitle = template.content.querySelector('div.panel-title');
as explained in the link you provided.

Bootstrap dropdown-toggle close on click

I have a dropdown button with a list of things that are anchored to different parts of the page. This dropdown is only for mobile.
However the problem is, the dropdown would not close after I click on it. Is there anyway I can make it close on click? I've tried looking around but it wouldn't work on mine.
<div id="mobile-dropdown" class="nav2 w" data-spy="affix" data-offset-top="350">
<div class="container">
<div class="pull-left" style="margin-top:3px; margin-right:3px;">Jump to </div>
<div class="pull-left">
<div class="btn-group mob-fl">
<button type="button" class="btn btn-default btn-sm dropdown-toggle" data-toggle="dropdown" aria-expanded="false">
Categories
<span class="caret"></span>
</button>
<ul class="dropdown-menu" role="menu">
<li>One</li>
<li>Two</li>
<li>Three</li>
<li>Four</li>
</ul>
</div>
</div>
</div>
</div>
I also took a look at bootstrap's js itself and caught this line:
if ('ontouchstart' in document.documentElement && !$parent.closest('.navbar-nav').length) {
// if mobile we use a backdrop because click events don't delegate
$('<div class="dropdown-backdrop"/>').insertAfter($(this)).on('click', clearMenus)
}
Is this be the reason why it won't close? Are there any workaround to make it work?
EDIT:
So with some help i got this script:
$('document').ready(function() {
$("a.dropdown-toggle").click(function(ev) {
$("a.dropdown-toggle").dropdown("toggle");
return false;
});
$("ul.dropdown-menu a").click(function(ev) {
$("a.dropdown-toggle").dropdown("toggle");
return false;
});
});
My javascript is pretty weak, how do i actually edit this to make it work only in my "mobile-dropdown" id div.
Alright so far I've updated my script to this:
$('document').ready(function() {
$("#subject_cat_mob .dropdown-toggle").click(function(ev) {
$("#subject_cat_mob .dropdown-toggle").dropdown("toggle");
return false;
});
$("#subject_cat_mob ul.dropdown-menu a").click(function(ev) {
$("#subject_cat_mob .dropdown-toggle").dropdown("toggle");
return false;
});
});
It works like how I want it to be. But the dropdown won't open again after the first time.
This should make it work for your HTML:
$('document').ready(function() {
$("#mobile-dropdown .dropdown-toggle").click(function() {
$(this).dropdown("toggle");
return false;
});
});
Update
Here's a working example including your smooth scroll functionality:
$(function() {
$('a[href*=#]:not([href=#])[href^="#"]:not([data-toggle])').click(function() {
$(this).dropdown("toggle"); // this is the important part!
if (location.pathname.replace(/^\//,'') == this.pathname.replace(/^\//,'') && location.hostname == this.hostname) {
var target = $(this.hash);
target = target.length ? target : $('[name=' + this.hash.slice(1) +']');
if (target.length) {
$('html,body').animate({
scrollTop: target.offset().top-100
}, 1000);
return false;
}
}
});
});
Notice the third line? That's all it needs: $(this).dropdown("toggle");.
You can check out a working example on JSFiddle.
A good solution can be found here:
https://github.com/CWSpear/bootstrap-hover-dropdown
Add class="dropdown-toggle disabled"
It's better explained here Allow click on twitter bootstrap dropdown toggle link?

Scraping links from html elements - casperjs

I am currently trying to scrape links and thumbnails from this SITE with the help of casperjs. I was able to easily figure out the html structure(shown below). I am trying to extract from all a tags the link found in the href attribute. I run my script but I get an error for video_links . How could I go about scraping all links and thumbnails and output in an array?
Error
TypeError: 'undefined' is not an object (evaluating 'video_links.length')
Script
var casper = require('casper').create({}),video_links,video_thumbnails;
//Functions
function getLinks() {
var element = document.querySelectorAll('.cne-episode-block a');
return Array.prototype.map.call(element, function(e) {
return e.getAttribute('href');
});
}
casper.start('http://video.wired.com/');
casper.then(function() {
video_links = this.evaluate(getLinks);
});
casper.run( this.echo(video_links.length + ' links found.') );
HTML
<div class="cne-thumb-grid-container cne-context-container">
<div class="cne-thumb cne-episode-block " data-videoid="551dc13461646d11aa020000">
<div class="cne-thumb-image cne-rollover" data-powertiptarget="551dc13461646d11aa020000">
<a class="cne-thumbnail cne-zoom-effect js-ajax-video-load" href="/watch/angry-nerd-will-netflix-s-daredevil-fly-or-flop" data-video-series="Angry Nerd" data-video-series-id="518d55c268f9dac897000003" data-video-id="551dc13461646d11aa020000" data-video-categories="[" Movies \u0026 TV "]">
<img class="cne-video-thumb" src="http://dwgyu36up6iuz.cloudfront.net/heru80fdn/image/upload/c_fill,d_placeholder_thescene.jpg,fl_progressive,g_face,h_151,q_80,w_270/v1428076783/wired_angry-nerd-will-netflix-s-daredevil-fly-or-flop.jpg" alt="Will Netflix’s Daredevil Fly or Flop?">
<div class="cne-thumbnail-play">Play</div>
</a>
</div>
<div class="cne-thumb-title the-thumb-title">
<a class="js-ajax-video-load" href="/watch/angry-nerd-will-netflix-s-daredevil-fly-or-flop" data-video-id="551dc13461646d11aa020000">Will Netflix’s Daredevil Fly or Flop?</a>
<div class="cne-thumb-subtitle">
Angry Nerd
</div>
</div>
<div id="551dc13461646d11aa020000" class="cne-thumb-rollover">
<div class="cne-thumb-rollover-box">
<span class="cne-rollover-category"> Movies & TV </span>
<span class="cne-rollover-name"> Will Netflix’s Daredevil Fly or Flop? </span>
<span class="cne-rollover-description"> If Netflix’s new Daredevil series is anything like Ben Affleck’s Daredevil film, we’re all in trouble. Angry Nerd explains what the latest incarnation needs to get right to make sure the man without fear doesn’t turn into a total flop. </span>
</div>
</div>
</div>
</div>
If the selectors are on the same level, you will only need one of them. So just use either cne-thumb or cne-episode-block in your querySelectorAll not both.

scrollTo in AngularJS

Until now I have always been using jQuery for my single page "scroll to div" applications, but since in making an Angular app (just for learning purposes) I try to do everything in angular instead of falling back on good ol' jQuery.
Im trying to make a scroll-to-div-on-the-same-page-menu, but Im not really sure on how to do this in Angular tho.
Currently I'm using this snippet to do what I want:
JS
app.directive('scrollOnClick', function() {
return {
restrict: 'A',
link: function(scope, $elm, attrs) {
$elm.click(function() {
var linkHref = attrs.href;
angular.element('html,body').animate({
// select the element the href points to
scrollTop: angular.element(linkHref).offset().top - 60
}, 'slow');
});
}
}
});
HTML
<div class="navbar navbar-default navbar-fixed-top" role="navigation">
<div class="container">
<div class="navbar-header">
<button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a ng-href="/" role="button" class="navbar-brand">angularApp</a>
</div>
<div class="navbar-collapse collapse">
<ul class="nav navbar-nav">
<li>One</li>
<li>Two</li>
<li>Three</li>
</ul>
</div>
</div>
</div>
<div id="page-wrap">
<div id="one">...</div>
<div id="two">...</div>
<div id="three">...</div>
</div>
But it doesn't work perfectly.
I need it to scroll with a margin of 60px as you can se in the code, because of my fixed navbar. I also want it to navigate slower and have a pretty url like /two instead of /#two.
How is this achieved?
If you have jQuery loaded already, you can use angular.element as an alias for $. That's usually the "proper" way to do it in angular. If you don't load jQuery first, angular.element uses jQLite, which is limited in terms of what selectors are available. Since you're already using jQuery, lets assume you already have it loaded up before angular.
Assuming that your jQuery example is working the way you like, you can just use the same selectors, substituting $elm for target, subtract the 60px and also set the animation timer to 1000 as you did in your jQuery example.
Try something like this:
app.directive('scrollOnClick', function() {
return {
restrict: 'A',
link: function(scope, $elm) {
$elm.click(function() {
// same as your jQuery example
angular.element('html,body').animate({
scrollTop: $elm.offset().top - 60
}, 1000);
});
}
}
});
--EDIT--
Wait, I see what you're trying to do here...
You need to get the href attribute from your element. The link function can take a third argument, which is the attributes attached to the element. Then instead of setting scrollTop to the offset of the link you clicked, you set it to the offset of the element in the href, using angular.element to select that. Like this:
app.directive('scrollOnClick', function() {
return {
restrict: 'A',
link: function(scope, $elm, attrs) {
$elm.click(function() {
var linkHref = attrs.href;
angular.element('html,body').animate({
// select the element the href points to
scrollTop: angular.element(linkHref).offset().top - 60
}, 1000);
});
}
}
});
I ended up using angular-scrollto which I think is very good. Very simple to use. All I had to do after I installed it with bower was to inject the module and the simply add this to my HTML (just an example from the github):
Go to element
<div id="element">
You will scroll to me
</div>

Categories

Resources