Related
I have a react application that needs to display some text with different interval between the sentences, one at a time.
So what I did is to put all the sentences in an array and create a setInterval function:
startShowingText() {
let i = 0
this.setState({
timer: window.setInterval(() => {
this.setState({
sentence: SENTENCES[i]
})
i += 1
}, this.state.startingDuration)
})
}
Unfortunately this approach has many downside:
1) It needs a check to terminate;
2) I can't modify the time dynamically, so once this.state.startingDuration is set, that's going to be the speed for all the sentences...
3) For some reasons, if I assign the window.setInterval function to a variable outside the state, that piece of code is not executed:
startShowingText() {
let i = 0
console.log('here')
const timer = window.setInterval(() => {
this.setState({
sentence: SENTENCES[i]
})
i += 1
}, this.state.startingDuration)
console.log('there')
}
This code was just printing:
here
there
Without triggering the actual timer.
How can I improve the code and make the interval dynamically adjustable?
One approach you could use borrows from the game development world where they create "game loops" that run at a certain tick interval. Within the loop, you could determine if it is time to output the sentence or not based on the current loop iteration.
A rough example:
const sentences = {
"My first sentence.": 1,
"My second sentence.": 20,
"My third sentence.": 25,
"My fourth sentence.": 40
};
const ticks = 100;
const maxLoops = 100;
const i = setInterval(writeLoop, ticks);
let loopCounter = 0;
function writeLoop() {
if (loopCounter > maxLoops) {
clearInterval(i);
}
for (sentence in sentences) {
let targetLoop = sentences[sentence];
if (loopCounter == targetLoop) {
document.write('<div>'+sentence+'</div>');
}
}
loopCounter = loopCounter + 1;
}
You can use setTimeout and recursive function like below;
class App extends React.Component {
sentences = ["sentence1", "sentence2", "sentence3"];
delayTime = [1000, 500, 3000];
constructor(props) {
super(props);
this.state = {
selected: 0,
}
}
componentDidMount() {
this.showSentence();
}
showSentence() {
const { selected } = this.state;
setTimeout(() => {
this.setState({ selected: (selected + 1) % 3});
this.showSentence();
}, this.delayTime[selected])
}
render() {
return (
<div>{this.sentences[this.state.selected]}</div>
);
}
}
ReactDOM.render(<App/>, document.getElementById('app'))
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id='app'/>
Result: sentence1 -1s-> sentence2 -0.5s-> sentence3 -3s-> sentence1 again
You can recursively call setTimeout and update the speed based on the current dialog block.
The speed for each dialog is letters typed per second.
var dialog = [{
text : "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.",
speed : 120
}, {
text : "Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.",
speed : 60
}, {
text : "Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.",
speed : 90
}, {
text : "Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.",
speed : 30
}];
let estimatedTime = dialog.reduce((d, e) => d + ((1000 / e.speed) * e.text.length) / 1000, 0);
console.log(`Estimated time: ${estimatedTime.toFixed(3)} seconds`);
var startTime = new Date().getTime();
typeDialog(dialog, document.getElementById('lorem'), () => {
let endTime = new Date().getTime();
let duration = (endTime - startTime) / 1000;
let latency = duration - estimatedTime;
console.log(`Task took: ${duration.toFixed(3)} seconds`);
console.log(`Latency: ${latency.toFixed(3)} seconds`);
});
function typeDialog(dialog, targetEl, completeFn) {
if (dialog.length > 0) {
let index = 0, speed = 1000 / dialog[index].speed,
currentParagraph = document.createElement('p');
targetEl.appendChild(currentParagraph);
typeCharacter(index, 0, speed);
function typeCharacter(index, letter, speed) {
if (letter > dialog[index].text.length) {
index++; letter = 0;
if (index < dialog.length) {
speed = 1000 / dialog[index].speed;
currentParagraph = document.createElement('p');
targetEl.appendChild(currentParagraph);
}
}
if (index < dialog.length) {
setTimeout(() => {
currentParagraph.textContent += dialog[index].text.charAt(letter);
typeCharacter(index, letter + 1, speed)
}, speed);
} else {
if (completeFn != null) {
completeFn.call();
}
}
}
}
}
.as-console-wrapper .as-console-row-code,
.as-console-wrapper .as-console-row::after,
#lorem p {
font-size: smaller;
}
<div id="lorem"></div>
Here is similar code, but less-coupled.
var dialog = [{
text : "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.",
speed : 120
}, {
text : "Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.",
speed : 60
}, {
text : "Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.",
speed : 90
}, {
text : "Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.",
speed : 30
}];
let estimatedTime = dialog.reduce((d, e) => d + ((1000 / e.speed) * e.text.length) / 1000, 0);
console.log(`Estimated time: ${estimatedTime.toFixed(3)} seconds`);
let startTime = new Date().getTime();
typeDialog(dialog, {
targetEl : document.getElementById('lorem'),
completeFn : () => {
let endTime = new Date().getTime();
let duration = (endTime - startTime) / 1000;
let latency = duration - estimatedTime;
console.log(`Task took: ${duration.toFixed(3)} seconds`);
console.log(`Latency: ${latency.toFixed(3)} seconds`);
}
});
// Wrapper - sets up options
function typeDialog(dialog, options) {
options = Object.assign({
list : dialog, row : 0, col : 0,
currentParagraph : document.createElement('p'),
speed : 1000 / dialog[0].speed,
targetEl : options.targetEl || ((el) => {
document.body.appendChild(el);
return el;
})(document.createElement('DIV'))
}, options || {});
options.targetEl.appendChild(options.currentParagraph);
repeater(typeDialogInner, options, true);
}
// Control - updated options and performs writing
function typeDialogInner(options) {
if (options.col > options.list[options.row].text.length) {
options.row++; options.col = 0;
if (options.row < options.list.length) {
options.currentParagraph = document.createElement('p');
options.speed = 1000 / options.list[options.row].speed;
options.targetEl.appendChild(options.currentParagraph);
}
}
if (options.row < options.list.length) {
options.currentParagraph.textContent += options.list[options.row].text.charAt(options.col++);
return true;
}
return false;
}
// Utility - convenience function to handle the looping
function repeater(fn, options, repeat) {
if (repeat) {
setTimeout(() => repeater(fn, options, fn(options)), options.speed);
} else {
if (options.completeFn != null) {
options.completeFn.call();
}
}
}
.as-console-wrapper .as-console-row-code,
.as-console-wrapper .as-console-row::after,
#lorem p {
font-size: smaller;
}
<div id="lorem"></div>
I have a slider(source code here) that currently has it's height set to 100%. However, I want the slider to have a height of 550px so it does not look too big, but am not managing to get that right for some reason.
Below is the full code and running snippet:
(function(factory){
if (typeof define === 'function' && define.amd) {
define(['jquery'], factory);
} else if (typeof exports !== 'undefined') {
module.exports = factory(require('jquery'));
} else {
factory(jQuery);
}
})(function($){
var Zippy = (function(element, settings){
var instanceUid = 0;
function _Zippy(element, settings){
this.defaults = {
slideDuration: '3000',
speed: 500,
arrowRight: '.arrow-right',
arrowLeft: '.arrow-left'
};
this.settings = $.extend({},this,this.defaults,settings);
this.initials = {
currSlide : 0,
$currSlide: null,
totalSlides : false,
csstransitions: false
};
$.extend(this,this.initials);
this.$el = $(element);
this.changeSlide = $.proxy(this.changeSlide,this);
this.init();
this.instanceUid = instanceUid++;
}
return _Zippy;
})();
Zippy.prototype.init = function(){
this.csstransitionsTest();
this.$el.addClass('zippy-carousel');
this.build();
this.events();
this.activate();
this.initTimer();
};
Zippy.prototype.csstransitionsTest = function(){
var elem = document.createElement('modernizr');
var props = ["transition","WebkitTransition","MozTransition","OTransition","msTransition"];
for ( var i in props ) {
var prop = props[i];
var result = elem.style[prop] !== undefined ? prop : false;
if (result){
this.csstransitions = result;
break;
}
}
};
Zippy.prototype.addCSSDuration = function(){
var _ = this;
this.$el.find('.slide').each(function(){
this.style[_.csstransitions+'Duration'] = _.settings.speed+'ms';
});
}
Zippy.prototype.removeCSSDuration = function(){
var _ = this;
this.$el.find('.slide').each(function(){
this.style[_.csstransitions+'Duration'] = '';
});
}
Zippy.prototype.build = function(){
var $indicators = this.$el.append('<ul class="indicators" >').find('.indicators');
this.totalSlides = this.$el.find('.slide').length;
for(var i = 0; i < this.totalSlides; i++) $indicators.append('<li data-index='+i+'>');
};
Zippy.prototype.activate = function(){
this.$currSlide = this.$el.find('.slide').eq(0);
this.$el.find('.indicators li').eq(0).addClass('active');
};
Zippy.prototype.events = function(){
$('body')
.on('click',this.settings.arrowRight,{direction:'right'},this.changeSlide)
.on('click',this.settings.arrowLeft,{direction:'left'},this.changeSlide)
.on('click','.indicators li',this.changeSlide);
};
Zippy.prototype.clearTimer = function(){
if (this.timer) clearInterval(this.timer);
};
Zippy.prototype.initTimer = function(){
this.timer = setInterval(this.changeSlide, this.settings.slideDuration);
};
Zippy.prototype.startTimer = function(){
this.initTimer();
this.throttle = false;
};
Zippy.prototype.changeSlide = function(e){e
if (this.throttle) return;
this.throttle = true;
this.clearTimer();
var direction = this._direction(e);
var animate = this._next(e,direction);
if (!animate) return;
var $nextSlide = this.$el.find('.slide').eq(this.currSlide).addClass(direction + ' active');
if (!this.csstransitions){
this._jsAnimation($nextSlide,direction);
} else {
this._cssAnimation($nextSlide,direction);
}
};
Zippy.prototype._direction = function(e){
var direction;
// Default to forward movement
if (typeof e !== 'undefined'){
direction = (typeof e.data === 'undefined' ? 'right' : e.data.direction);
} else {
direction = 'right';
}
return direction;
};
Zippy.prototype._next = function(e,direction){
var index = (typeof e !== 'undefined' ? $(e.currentTarget).data('index') : undefined);
switch(true){
case( typeof index !== 'undefined'):
if (this.currSlide == index){
this.startTimer();
return false;
}
this.currSlide = index;
break;
case(direction == 'right' && this.currSlide < (this.totalSlides - 1)):
this.currSlide++;
break;
case(direction == 'right'):
this.currSlide = 0;
break;
case(direction == 'left' && this.currSlide === 0):
this.currSlide = (this.totalSlides - 1);
break;
case(direction == 'left'):
this.currSlide--;
break;
}
return true;
};
Zippy.prototype._cssAnimation = function($nextSlide,direction){
setTimeout(function(){
this.$el.addClass('transition');
this.addCSSDuration();
this.$currSlide.addClass('shift-'+direction);
}.bind(this),100);
setTimeout(function(){
this.$el.removeClass('transition');
this.removeCSSDuration();
this.$currSlide.removeClass('active shift-left shift-right');
this.$currSlide = $nextSlide.removeClass(direction);
this._updateIndicators();
this.startTimer();
}.bind(this),100 + this.settings.speed);
};
Zippy.prototype._jsAnimation = function($nextSlide,direction){
var _ = this;
if(direction == 'right') _.$currSlide.addClass('js-reset-left');
var animation = {};
animation[direction] = '0%';
var animationPrev = {};
animationPrev[direction] = '100%';
this.$currSlide.animate(animationPrev,this.settings.speed);
$nextSlide.animate(animation,this.settings.speed,'swing',function(){
_.$currSlide.removeClass('active js-reset-left').attr('style','');
_.$currSlide = $nextSlide.removeClass(direction).attr('style','');
_._updateIndicators();
_.startTimer();
});
};
Zippy.prototype._updateIndicators = function(){
this.$el.find('.indicators li').removeClass('active').eq(this.currSlide).addClass('active');
};
$.fn.Zippy = function(options){
return this.each(function(index,el){
el.Zippy = new Zippy(el,options);
});
};
});
var args = {
arrowRight : '.arrow-right',
arrowLeft : '.arrow-left',
speed : 1000,
slideDuration : 4000
};
$('.carousel').Zippy(args);
(function(factory){
if (typeof define === 'function' && define.amd) {
define(['jquery'], factory);
} else if (typeof exports !== 'undefined') {
module.exports = factory(require('jquery'));
} else {
factory(jQuery);
}
})(function($){
var Zippy = (function(element, settings){
var instanceUid = 0;
function _Zippy(element, settings){
this.defaults = {
slideDuration: '3000',
speed: 500,
arrowRight: '.arrow-right',
arrowLeft: '.arrow-left'
};
this.settings = $.extend({},this,this.defaults,settings);
this.initials = {
currSlide : 0,
$currSlide: null,
totalSlides : false,
csstransitions: false
};
$.extend(this,this.initials);
this.$el = $(element);
this.changeSlide = $.proxy(this.changeSlide,this);
this.init();
this.instanceUid = instanceUid++;
}
return _Zippy;
})();
Zippy.prototype.init = function(){
this.csstransitionsTest();
this.$el.addClass('zippy-carousel');
this.build();
this.events();
this.activate();
this.initTimer();
};
Zippy.prototype.csstransitionsTest = function(){
var elem = document.createElement('modernizr');
var props = ["transition","WebkitTransition","MozTransition","OTransition","msTransition"];
for ( var i in props ) {
var prop = props[i];
var result = elem.style[prop] !== undefined ? prop : false;
if (result){
this.csstransitions = result;
break;
}
}
};
Zippy.prototype.addCSSDuration = function(){
var _ = this;
this.$el.find('.slide').each(function(){
this.style[_.csstransitions+'Duration'] = _.settings.speed+'ms';
});
}
Zippy.prototype.removeCSSDuration = function(){
var _ = this;
this.$el.find('.slide').each(function(){
this.style[_.csstransitions+'Duration'] = '';
});
}
Zippy.prototype.build = function(){
var $indicators = this.$el.append('<ul class="indicators" >').find('.indicators');
this.totalSlides = this.$el.find('.slide').length;
for(var i = 0; i < this.totalSlides; i++) $indicators.append('<li data-index='+i+'>');
};
Zippy.prototype.activate = function(){
this.$currSlide = this.$el.find('.slide').eq(0);
this.$el.find('.indicators li').eq(0).addClass('active');
};
Zippy.prototype.events = function(){
$('body')
.on('click',this.settings.arrowRight,{direction:'right'},this.changeSlide)
.on('click',this.settings.arrowLeft,{direction:'left'},this.changeSlide)
.on('click','.indicators li',this.changeSlide);
};
Zippy.prototype.clearTimer = function(){
if (this.timer) clearInterval(this.timer);
};
Zippy.prototype.initTimer = function(){
this.timer = setInterval(this.changeSlide, this.settings.slideDuration);
};
Zippy.prototype.startTimer = function(){
this.initTimer();
this.throttle = false;
};
Zippy.prototype.changeSlide = function(e){e
if (this.throttle) return;
this.throttle = true;
this.clearTimer();
var direction = this._direction(e);
var animate = this._next(e,direction);
if (!animate) return;
var $nextSlide = this.$el.find('.slide').eq(this.currSlide).addClass(direction + ' active');
if (!this.csstransitions){
this._jsAnimation($nextSlide,direction);
} else {
this._cssAnimation($nextSlide,direction);
}
};
Zippy.prototype._direction = function(e){
var direction;
// Default to forward movement
if (typeof e !== 'undefined'){
direction = (typeof e.data === 'undefined' ? 'right' : e.data.direction);
} else {
direction = 'right';
}
return direction;
};
Zippy.prototype._next = function(e,direction){
var index = (typeof e !== 'undefined' ? $(e.currentTarget).data('index') : undefined);
switch(true){
case( typeof index !== 'undefined'):
if (this.currSlide == index){
this.startTimer();
return false;
}
this.currSlide = index;
break;
case(direction == 'right' && this.currSlide < (this.totalSlides - 1)):
this.currSlide++;
break;
case(direction == 'right'):
this.currSlide = 0;
break;
case(direction == 'left' && this.currSlide === 0):
this.currSlide = (this.totalSlides - 1);
break;
case(direction == 'left'):
this.currSlide--;
break;
}
return true;
};
Zippy.prototype._cssAnimation = function($nextSlide,direction){
setTimeout(function(){
this.$el.addClass('transition');
this.addCSSDuration();
this.$currSlide.addClass('shift-'+direction);
}.bind(this),100);
setTimeout(function(){
this.$el.removeClass('transition');
this.removeCSSDuration();
this.$currSlide.removeClass('active shift-left shift-right');
this.$currSlide = $nextSlide.removeClass(direction);
this._updateIndicators();
this.startTimer();
}.bind(this),100 + this.settings.speed);
};
Zippy.prototype._jsAnimation = function($nextSlide,direction){
var _ = this;
if(direction == 'right') _.$currSlide.addClass('js-reset-left');
var animation = {};
animation[direction] = '0%';
var animationPrev = {};
animationPrev[direction] = '100%';
this.$currSlide.animate(animationPrev,this.settings.speed);
$nextSlide.animate(animation,this.settings.speed,'swing',function(){
_.$currSlide.removeClass('active js-reset-left').attr('style','');
_.$currSlide = $nextSlide.removeClass(direction).attr('style','');
_._updateIndicators();
_.startTimer();
});
};
Zippy.prototype._updateIndicators = function(){
this.$el.find('.indicators li').removeClass('active').eq(this.currSlide).addClass('active');
};
$.fn.Zippy = function(options){
return this.each(function(index,el){
el.Zippy = new Zippy(el,options);
});
};
});
var args = {
arrowRight : '.arrow-right',
arrowLeft : '.arrow-left',
speed : 1000,
slideDuration : 4000
};
$('.carousel').Zippy(args);
<div class="wrapper">
<div class="carousel">
<div class="inner">
<div class="slide active">
<div class="slide-box">
<h1>1</h1>
<h2>Heading 2</h2>
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod
tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,
quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
consequat.</p>
</div>
</div>
<div class="slide">
<div class="slide-box">
<h1>2</h1>
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod
tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,
quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
consequat.</p>
</div>
</div>
<div class="slide">
<div class="slide-box">
<h1>3</h1>
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod
tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,
quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
consequat.</p>
</div>
</div>
</div>
<div class="arrow arrow-left"></div>
<div class="arrow arrow-right"></div>
</div>
</div>
I tried giving a height of 500px to the wrapper class but it does not seem to be the best way.
How can I adjust the height of the slider and also keep the content in the middle of the box when I resize the page?
Here mycodepen if needed
Thank you in advance
The padding in the slide style give problems with height:
.slide {
text-align: center;
padding-top: 25%;
background-size: cover;
}
This is my fix to your style to get the right height, I also centered horizontally and vertically the slide-box and the text:
/********For better see the text********/
p{
color:white;
}
h2{
color:white;
}
/**************************************/
.wrapper{
width:100%;
position:relative;
height: 50px;
}
.slide-box {
max-width: 1300px;
margin: 0 auto;
padding: 54px;
position: relative;
top: 50%;
-webkit-transform: translateY(-50%);
-ms-transform: translateY(-50%);
transform: translateY(-50%);
}
/**
* Padding is set relative to the width
* of the element, so here padding-top:60% is
* a percentage of the width. This allows us
* to set the height as a ratio of the width
*
*/
.carousel{
width: 100%;
height: 500px;
position: relative;
/* padding-top: 50%; */
overflow: hidden;
}
.inner{
width: 100%;
height: 100%;
position: absolute;
top:0;
left: 0;
}
/**
* ==========================
* Animation styles
*
* Notes:
* 1. We use z-index to position active slides in-front
* of non-active slides
* 2. We set right:0 and left:0 on .slide to provide us with
* a default positioning on both sides of the slide. This allows
* us to trigger JS and CSS3 animations easily
*
*/
.slide{
width: 100%;
height: 500px;
position: absolute;
top:0;
right:0;
left:0;
z-index: 1;
opacity: 0;
}
.slide.active,
.slide.left,
.slide.right{
z-index: 2;
opacity: 1;
}
/**
* ==========================
* JS animation styles
*
* We use jQuery.animate to control the sliding animations
* when CSS3 animations are not available. In order for
* the next slide to slide in from the right, we need
* to change the left:0 property of the slide to left:auto
*
*/
.js-reset-left{left:auto}
/**
* ==========================
* CSS animation styles
*
* .slide.left and .slide.right set-up
* the to-be-animated slide so that it can slide
* into view. For example, a slide that is about
* to slide in from the right will:
* 1. Be positioned to the right of the viewport (right:-100%)
* 2. Slide in when the style is superseded with a more specific style (right:0%)
*
*/
.slide.left{
left:-100%;
right:0;
}
.slide.right{
right:-100%;
left: auto;
}
.transition .slide.left{left:0%}
.transition .slide.right{right:0%}
/**
* The following classes slide the previously active
* slide out of view before positioning behind the
* currently active slide
*
*/
.transition .slide.shift-right{right: 100%;left:auto}
.transition .slide.shift-left{left: 100%;right:auto}
/**
* This sets the CSS properties that will animate. We set the
* transition-duration property dynamically via JS.
* We use the browser's default transition-timing-function
* for simplicity's sake
*
* It is important to note that we are using CodePen's inbuilt
* CSS3 property prefixer. For your own projects, you will need
* to prefix the transition and transform properties here to ensure
* reliable support across browsers
*
*/
.transition .slide{
transition-property: right, left, margin;
}
/**
* ==========================
* Indicators
*
*/
.indicators{
width:100%;
position: absolute;
bottom:0;
z-index: 4;
padding:0;
text-align: center;
}
.indicators li{
width: 13px;
height: 13px;
display: inline-block;
margin: 5px;
background: #fff;
list-style-type: none;
border-radius: 50%;
cursor:pointer;
transition:background 0.3s ease-out;
}
.indicators li.active{background:#93278f}
.indicators li:hover{background-color:#2b2b2b}
/**
* ==========================
* Arrows
*
*/
.arrow{
width: 20px;
height: 20px;
position:absolute;
top:50%;
z-index:5;
border-top:3px solid #fff;
border-right:3px solid #fff;
cursor:pointer;
transition:border-color 0.3s ease-out;
}
.arrow:hover{border-color:#93278f}
.arrow-left{
left:20px;
transform:rotate(225deg);
}
.arrow-right{
right:20px;
transform:rotate(45deg);
}
/**
* ==========================
* For demo purposes only
*
* Please note that the styles below are used
* for the purposes of this demo only. There is no need
* to use these in any of your own projects
*
*/
.slide{
text-align:left;
background-size:cover;
display: table-cell;
vertical-align: middle;
}
h1{
width:100px;
height:100px;
background-color:rgba(146, 45, 141,0.7);
margin:auto;
line-height:100px;
color:#fff;
font-size:2.4em;
border-radius:50%;
text-align: center;
}
.slide:nth-child(1){
background-image:url(https://s3-us-west-2.amazonaws.com/s.cdpn.io/163697/slide-1.jpg);
}
.slide:nth-child(2){
background-image:url(https://s3-us-west-2.amazonaws.com/s.cdpn.io/163697/slide-2.jpg);
}
.slide:nth-child(3){
background-image:url(https://s3-us-west-2.amazonaws.com/s.cdpn.io/163697/slide-3.jpg);
}
Building a Bootstrap template for a responsive site. It needs to show a simple horizontal navbar on desktop and tablet, then go to offcanvas slide-in on phones. Found an example by Phil Hughes (iamphill) on Github. As I adapted this, the dropdown menu items stopped working. When I click on either of the two dropdowns nothing happens. No errors in Chrome Inspector. Validating HTML, CSS and JS does not reveal anything. The bug is either too obvious or too subtle.
! function(t) {
"use strict";
"function" == typeof define && define.amd ? define(["jquery"], t) : "object" == typeof exports ? module.exports = t(require("jquery")) : t(jQuery)
}(function(t) {
"use strict";
function e(e) {
var o = e.attr("data-target");
o || (o = e.attr("href"), o = o && /#[A-Za-z]/.test(o) && o.replace(/.*(?=#[^\s]*$)/, ""));
var n = o && t(o);
return n && n.length ? n : e.parent()
}
function o(o) {
o && 3 === o.which || (t(r).remove(), t(a).each(function() {
var n = t(this),
r = e(n),
a = {
relatedTarget: this
};
r.hasClass("open") && (o && "click" == o.type && /input|textarea/i.test(o.target.tagName) && t.contains(r[0], o.target) || (r.trigger(o = t.Event("hide.bs.dropdown", a)), o.isDefaultPrevented() || (n.attr("aria-expanded", "false"), r.removeClass("open").trigger(t.Event("hidden.bs.dropdown", a)))))
}))
}
function n(e) {
return this.each(function() {
var o = t(this),
n = o.data("bs.dropdown");
n || o.data("bs.dropdown", n = new i(this)), "string" == typeof e && n[e].call(o)
})
}
var r = ".dropdown-backdrop",
a = '[data-toggle="dropdown"]',
d = ".drawer-nav",
i = function(e) {
t(e).on("click.bs.dropdown", this.toggle)
};
i.VERSION = "3.3.5", i.prototype.toggle = function(n) {
var r = t(this);
if (!r.is(".disabled, :disabled")) {
var a = e(r),
i = a.hasClass("open");
if (o(), !i) {
"ontouchstart" in document.documentElement && !a.closest(d).length && t(document.createElement("div")).addClass("dropdown-backdrop").insertAfter(t(this)).on("click", o);
var s = {
relatedTarget: this
};
if (a.trigger(n = t.Event("show.bs.dropdown", s)), n.isDefaultPrevented()) return;
r.trigger("focus").attr("aria-expanded", "true"), a.toggleClass("open").trigger(t.Event("shown.bs.dropdown", s))
}
return !1
}
}, i.prototype.keydown = function(o) {
if (/(38|40|27|32)/.test(o.which) && !/input|textarea/i.test(o.target.tagName)) {
var n = t(this);
if (o.preventDefault(), o.stopPropagation(), !n.is(".disabled, :disabled")) {
var r = e(n),
d = r.hasClass("open");
if (!d && 27 != o.which || d && 27 == o.which) return 27 == o.which && r.find(a).trigger("focus"), n.trigger("click");
var i = " li:not(.disabled):visible a",
s = r.find(".dropdown-menu" + i);
if (s.length) {
var p = s.index(o.target);
38 == o.which && p > 0 && p--, 40 == o.which && p < s.length - 1 && p++, ~p || (p = 0), s.eq(p).trigger("focus")
}
}
}
};
var s = t.fn.dropdown;
t.fn.dropdown = n, t.fn.dropdown.Constructor = i, t.fn.dropdown.noConflict = function() {
return t.fn.dropdown = s, this
}, t(document).on("click.bs.dropdown.data-api", o).on("click.bs.dropdown.data-api", ".dropdown form", function(t) {
t.stopPropagation()
}).on("click.bs.dropdown.data-api", a, i.prototype.toggle).on("keydown.bs.dropdown.data-api", a, i.prototype.keydown).on("keydown.bs.dropdown.data-api", ".dropdown-menu", i.prototype.keydown)
});
// and now js for offcanvas
(function() {
var bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
(function($, window) {
var Offcanvas, OffcanvasDropdown, OffcanvasTouch, transformCheck;
OffcanvasDropdown = (function() {
function OffcanvasDropdown(element) {
this.element = element;
this._clickEvent = bind(this._clickEvent, this);
this.element = $(this.element);
this.nav = this.element.closest(".nav");
this.dropdown = this.element.parent().find(".dropdown-menu");
this.element.on('click', this._clickEvent);
this.nav.closest('.navbar-offcanvas').on('click', (function(_this) {
return function() {
if (_this.dropdown.is('.shown')) {
return _this.dropdown.removeClass('shown').closest('.active').removeClass('active');
}
};
})(this));
}
OffcanvasDropdown.prototype._clickEvent = function(e) {
if (!this.dropdown.hasClass('shown')) {
e.preventDefault();
}
e.stopPropagation();
$('.dropdown-toggle').not(this.element).closest('.active').removeClass('active').find('.dropdown-menu').removeClass('shown');
this.dropdown.toggleClass("shown");
return this.element.parent().toggleClass('active');
};
return OffcanvasDropdown;
})();
OffcanvasTouch = (function() {
function OffcanvasTouch(button, element, location, offcanvas) {
this.button = button;
this.element = element;
this.location = location;
this.offcanvas = offcanvas;
this._getFade = bind(this._getFade, this);
this._getCss = bind(this._getCss, this);
this._touchEnd = bind(this._touchEnd, this);
this._touchMove = bind(this._touchMove, this);
this._touchStart = bind(this._touchStart, this);
this.endThreshold = 130;
this.startThreshold = this.element.hasClass('navbar-offcanvas-right') ? $("body").outerWidth() - 60 : 20;
this.maxStartThreshold = this.element.hasClass('navbar-offcanvas-right') ? $("body").outerWidth() - 20 : 60;
this.currentX = 0;
this.fade = this.element.hasClass('navbar-offcanvas-fade') ? true : false;
$(document).on("touchstart", this._touchStart);
$(document).on("touchmove", this._touchMove);
$(document).on("touchend", this._touchEnd);
}
OffcanvasTouch.prototype._touchStart = function(e) {
this.startX = e.originalEvent.touches[0].pageX;
if (this.element.is('.in')) {
return this.element.height($(window).outerHeight());
}
};
OffcanvasTouch.prototype._touchMove = function(e) {
var x;
if ($(e.target).parents('.navbar-offcanvas').length > 0) {
return true;
}
if (this.startX > this.startThreshold && this.startX < this.maxStartThreshold) {
e.preventDefault();
x = e.originalEvent.touches[0].pageX - this.startX;
x = this.element.hasClass('navbar-offcanvas-right') ? -x : x;
if (Math.abs(x) < this.element.outerWidth()) {
this.element.css(this._getCss(x));
return this.element.css(this._getFade(x));
}
} else if (this.element.hasClass('in')) {
e.preventDefault();
x = e.originalEvent.touches[0].pageX + (this.currentX - this.startX);
x = this.element.hasClass('navbar-offcanvas-right') ? -x : x;
if (Math.abs(x) < this.element.outerWidth()) {
this.element.css(this._getCss(x));
return this.element.css(this._getFade(x));
}
}
};
OffcanvasTouch.prototype._touchEnd = function(e) {
var end, sendEvents, x;
if ($(e.target).parents('.navbar-offcanvas').length > 0) {
return true;
}
sendEvents = false;
x = e.originalEvent.changedTouches[0].pageX;
if (Math.abs(x) === this.startX) {
return;
}
end = this.element.hasClass('navbar-offcanvas-right') ? Math.abs(x) > (this.endThreshold + 50) : x < (this.endThreshold + 50);
if (this.element.hasClass('in') && end) {
this.currentX = 0;
this.element.removeClass('in').css(this._clearCss());
this.button.removeClass('is-open');
sendEvents = true;
} else if (Math.abs(x - this.startX) > this.endThreshold && this.startX > this.startThreshold && this.startX < this.maxStartThreshold) {
this.currentX = this.element.hasClass('navbar-offcanvas-right') ? -this.element.outerWidth() : this.element.outerWidth();
this.element.toggleClass('in').css(this._clearCss());
this.button.toggleClass('is-open');
sendEvents = true;
} else {
this.element.css(this._clearCss());
}
return this.offcanvas.bodyOverflow(sendEvents);
};
OffcanvasTouch.prototype._getCss = function(x) {
x = this.element.hasClass('navbar-offcanvas-right') ? -x : x;
return {
"-webkit-transform": "translate3d(" + x + "px, 0px, 0px)",
"-webkit-transition-duration": "0s",
"-moz-transform": "translate3d(" + x + "px, 0px, 0px)",
"-moz-transition": "0s",
"-o-transform": "translate3d(" + x + "px, 0px, 0px)",
"-o-transition": "0s",
"transform": "translate3d(" + x + "px, 0px, 0px)",
"transition": "0s"
};
};
OffcanvasTouch.prototype._getFade = function(x) {
if (this.fade) {
return {
"opacity": x / this.element.outerWidth()
};
} else {
return {};
}
};
OffcanvasTouch.prototype._clearCss = function() {
return {
"-webkit-transform": "",
"-webkit-transition-duration": "",
"-moz-transform": "",
"-moz-transition": "",
"-o-transform": "",
"-o-transition": "",
"transform": "",
"transition": "",
"opacity": ""
};
};
return OffcanvasTouch;
})();
window.Offcanvas = Offcanvas = (function() {
function Offcanvas(element) {
var t, target;
this.element = element;
this.bodyOverflow = bind(this.bodyOverflow, this);
this._sendEventsAfter = bind(this._sendEventsAfter, this);
this._sendEventsBefore = bind(this._sendEventsBefore, this);
this._documentClicked = bind(this._documentClicked, this);
this._close = bind(this._close, this);
this._open = bind(this._open, this);
this._clicked = bind(this._clicked, this);
this._navbarHeight = bind(this._navbarHeight, this);
target = this.element.attr('data-target') ? this.element.attr('data-target') : false;
if (target) {
this.target = $(target);
if (this.target.length && !this.target.hasClass('js-offcanvas-done')) {
this.element.addClass('js-offcanvas-has-events');
this.location = this.target.hasClass("navbar-offcanvas-right") ? "right" : "left";
this.target.addClass(transform ? "offcanvas-transform js-offcanvas-done" : "offcanvas-position js-offcanvas-done");
this.target.data('offcanvas', this);
this.element.on("click", this._clicked);
this.target.on('transitionend', (function(_this) {
return function() {
if (_this.target.is(':not(.in)')) {
return _this.target.height('');
}
};
})(this));
$(document).on("click", this._documentClicked);
if (this.target.hasClass('navbar-offcanvas-touch')) {
t = new OffcanvasTouch(this.element, this.target, this.location, this);
}
this.target.find(".dropdown-toggle").each(function() {
var d;
return d = new OffcanvasDropdown(this);
});
this.target.on('offcanvas.toggle', (function(_this) {
return function(e) {
return _this._clicked(e);
};
})(this));
this.target.on('offcanvas.close', (function(_this) {
return function(e) {
return _this._close(e);
};
})(this));
this.target.on('offcanvas.open', (function(_this) {
return function(e) {
return _this._open(e);
};
})(this));
}
} else {
console.warn('Offcanvas: `data-target` attribute must be present.');
}
}
Offcanvas.prototype._navbarHeight = function() {
if (this.target.is('.in')) {
return this.target.height($(window).outerHeight());
}
};
Offcanvas.prototype._clicked = function(e) {
e.preventDefault();
this._sendEventsBefore();
$(".navbar-offcanvas").not(this.target).trigger('offcanvas.close');
this.target.toggleClass('in');
this.element.toggleClass('is-open');
this._navbarHeight();
return this.bodyOverflow();
};
Offcanvas.prototype._open = function(e) {
e.preventDefault();
if (this.target.is('.in')) {
return;
}
this._sendEventsBefore();
this.target.addClass('in');
this.element.addClass('is-open');
this._navbarHeight();
return this.bodyOverflow();
};
Offcanvas.prototype._close = function(e) {
e.preventDefault();
if (this.target.is(':not(.in)')) {
return;
}
this._sendEventsBefore();
this.target.removeClass('in');
this.element.removeClass('is-open');
this._navbarHeight();
return this.bodyOverflow();
};
Offcanvas.prototype._documentClicked = function(e) {
var clickedEl;
clickedEl = $(e.target);
if (!clickedEl.hasClass('offcanvas-toggle') && clickedEl.parents('.offcanvas-toggle').length === 0 && clickedEl.parents('.navbar-offcanvas').length === 0 && !clickedEl.hasClass('navbar-offcanvas')) {
if (this.target.hasClass('in')) {
e.preventDefault();
this._sendEventsBefore();
this.target.removeClass('in');
this.element.removeClass('is-open');
this._navbarHeight();
return this.bodyOverflow();
}
}
};
Offcanvas.prototype._sendEventsBefore = function() {
if (this.target.hasClass('in')) {
return this.target.trigger('hide.bs.offcanvas');
} else {
return this.target.trigger('show.bs.offcanvas');
}
};
Offcanvas.prototype._sendEventsAfter = function() {
if (this.target.hasClass('in')) {
return this.target.trigger('shown.bs.offcanvas');
} else {
return this.target.trigger('hidden.bs.offcanvas');
}
};
Offcanvas.prototype.bodyOverflow = function(events) {
if (events === null) {
events = true;
}
if (this.target.is('.in')) {
$('body').addClass('offcanvas-stop-scrolling');
} else {
$('body').removeClass('offcanvas-stop-scrolling');
}
if (events) {
return this._sendEventsAfter();
}
};
return Offcanvas;
})();
transformCheck = (function(_this) {
return function() {
var asSupport, el, regex, translate3D;
el = document.createElement('div');
translate3D = "translate3d(0px, 0px, 0px)";
regex = /translate3d\(0px, 0px, 0px\)/g;
el.style.cssText = "-webkit-transform: " + translate3D + "; -moz-transform: " + translate3D + "; -o-transform: " + translate3D + "; transform: " + translate3D;
asSupport = el.style.cssText.match(regex);
return _this.transform = asSupport.length !== null;
};
})(this);
return $(function() {
transformCheck();
$('[data-toggle="offcanvas"]').each(function() {
var oc;
return oc = new Offcanvas($(this));
});
$(window).on('resize', function() {
$('.navbar-offcanvas.in').each(function() {
return $(this).height('').removeClass('in');
});
return $('.offcanvas-toggle').removeClass('is-open');
});
return $('.offcanvas-toggle').each(function() {
return $(this).on('click', function(e) {
var el, selector;
if (!$(this).hasClass('js-offcanvas-has-events')) {
selector = $(this).attr('data-target');
el = $(selector);
if (el) {
el.height('');
el.removeClass('in');
return $('body').css({
overflow: '',
position: ''
});
}
}
});
});
});
})(window.jQuery, window);
}).call(this);
/* CSS used here will be applied after bootstrap.css */
#media (max-width: 767px) {
.offcanvas-stop-scrolling {
height: 100%;
overflow: hidden; }
.navbar-default .navbar-offcanvas {
background-color: #f8f8f8; }
.navbar-inverse .navbar-offcanvas {
background-color: #222; }
.navbar-offcanvas {
position: fixed;
width: 100%;
max-width: 250px;
left: -250px;
top: 0;
padding-left: 15px;
padding-right: 15px;
z-index: 999;
overflow: scroll;
-webkit-overflow-scrolling: touch;
-webkit-transition: all 0.15s ease-in;
transition: all 0.15s ease-in; }
.navbar-offcanvas.in {
box-shadow: 0 0 20px rgba(0, 0, 0, 0.3); }
.navbar-offcanvas.navbar-offcanvas-fade {
opacity: 0; }
.navbar-offcanvas.navbar-offcanvas-fade.in {
opacity: 1; }
.navbar-offcanvas.offcanvas-transform.in {
-webkit-transform: translateX(250px);
transform: translateX(250px); }
.navbar-offcanvas.offcanvas-position.in {
left: 0; }
.navbar-offcanvas.navbar-offcanvas-right {
left: auto;
right: -250px; }
.navbar-offcanvas.navbar-offcanvas-right.offcanvas-transform.in {
-webkit-transform: translateX(-250px);
transform: translateX(-250px); }
.navbar-offcanvas.navbar-offcanvas-right.offcanvas-position.in {
left: auto;
right: 0; }
.navbar-offcanvas .dropdown.active .caret {
border-top: 0;
border-bottom: 4px solid; }
.navbar-offcanvas .dropdown-menu {
position: relative;
width: 100%;
border: inherit;
box-shadow: none;
-webkit-transition: height 0.15s ease-in;
transition: height 0.15s ease-in; }
.navbar-offcanvas .dropdown-menu.shown {
display: block;
margin-bottom: 10px; } }
.offcanvas-toggle .icon-bar {
background: #000;
-webkit-transition: all .25s ease-in-out;
transition: all .25s ease-in-out; }
.offcanvas-toggle.is-open .icon-bar:nth-child(1) {
-webkit-transform: rotate(45deg) translate(5px, 4px);
transform: rotate(45deg) translate(5px, 4px); }
.offcanvas-toggle.is-open .icon-bar:nth-child(2) {
opacity: 0; }
.offcanvas-toggle.is-open .icon-bar:nth-child(3) {
-webkit-transform: rotate(-45deg) translate(4px, -4px);
transform: rotate(-45deg) translate(4px, -4px); }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.0/jquery.min.js"></script>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
<!-- Optional theme -->
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap-theme.min.css" integrity="sha384-rHyoN1iRsVXV4nD0JutlnGaslCJuC7uwjduW9SVrLvRYooPp2bWYgmgJQIXwl/Sp" crossorigin="anonymous">
<!-- Latest compiled and minified JavaScript -->
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js" integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa" crossorigin="anonymous"></script>
<body class="body-offcanvas">
<header class="clearfix">
<nav class="navbar navbar-default">
<div class="container-fluid">
<div class="navbar-header">
<button type="button" class="navbar-toggle offcanvas-toggle" data-toggle="offcanvas" data-target="#js-bootstrap-offcanvas">
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="#">Brandhere</a>
</div>
<div class="navbar-offcanvas navbar-offcanvas-touch" id="js-bootstrap-offcanvas">
<ul class="nav navbar-nav">
<li class="dropdown">
<a class="dropdown-toggle" data-toggle="dropdown" href="#">NUMBERS<span class="caret"></span></a>
<ul class="dropdown-menu">
<li>One</li>
<li>Two</li>
<li>Thirty-six</li>
<li>-7</li>
<li>Eighteen</li>
</ul>
</li>
<li class="dropdown">
<a class="dropdown-toggle" data-toggle="dropdown" href="#">MUSIC<span class="caret"></span></a>
<ul class="dropdown-menu">
<li>BJ Cole</li>
<li>C. Debussy</li>
<li>Brian Eno</li>
<li>Robert Fripp</li>
<li>Skip James</li>
</ul>
</li>
</ul>
</div>
</div>
</nav>
</header>
<div class="container">
<div class="row">
<div>
<h1>Page Title</h1>
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin feugiat hendrerit feugiat. In cursus nisl id arcu ullamcorper, eget euismod ante tincidunt. Aliquam tincidunt felis eget quam euismod cursus. Nam aliquet a tellus ut pretium. Pellentesque fermentum nulla tempus mauris sagittis, eget imperdiet quam tristique. Pellentesque quis mauris mauris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Praesent sodales turpis fringilla ligula rutrum, eget mattis justo bibendum. Integer imperdiet mi non cursus bibendum. Nullam vitae cursus justo. Integer quis elit sit amet arcu pellentesque sit amet a sapien. Aliquam tincidunt felis eget quam euismod cursus. Suspendisse lobortis ut elit vitae rhoncus. Ut tincidunt, ante eu egestas sodales, dui nulla aliquet mi, a eleifend lacus risus sit amet lacus.</p>
</div>
</div>
</div>
</body>
Here it is on Bootply http://www.bootply.com/ONyk2E5GWv
There are 2 things here:
1) You're using same class for dropdown-toggle for both Numbers as well as Music. Try to keep it unique like below -
<li class="dropdown numbers">
<a class="dropdown-toggle" data-toggle="numbers" href="#">NUMBERS<span class="caret"></span></a>
<ul class="dropdown-menu">
<li>One</li>
</ul>
</li>
<li class="dropdown music">
<a class="dropdown-toggle" data-toggle="music" href="#">MUSIC<span class="caret"></span></a>
<ul class="dropdown-menu">
<li>BJ Cole</li>
</ul>
</li>
2) You're using CSS Media Query max-width: 767px which means that this styling is applied only for screens whose width are less than 767 pixels, ideally Tablets and Mobile devices. Upon click of dropdown, on the dropdown-menu, 'shown' class is being added which will have the styling of displaying the list by changing the CSS attribute display from 'none' to 'block'
.navbar-offcanvas .dropdown-menu.shown {
display: block;
margin-bottom: 10px;
}
How can I stop this carousel from bubbling the animation? If you click right faster, it will start to mix up things. I need to stop the event handler function if the animation is running, inside that timeOut, the handlers should go offline.
Please see snippet below:
var Carousel = function(element, options) {
this.carousel = document.querySelector(element);
this.slides = Array.prototype.slice.call(this.carousel.querySelectorAll('.item'), null);
this.prev = this.carousel.querySelector("[data-slide='prev']");
this.next = this.carousel.querySelector("[data-slide='next']");
this.indicators = this.carousel.querySelectorAll(".carousel-indicators li");
this.interval = options && options.interval ? options.interval : 5000;
this.duration = 600; // bootstrap carousel default transition duration
this.paused = null;
this.direction = null;
this.index = 0;
this.total = this.slides.length;
this.init();
};
Carousel.prototype = {
init: function() {
this.cycle();
this.actions();
},
_slideTo: function(next, e) {
var self = this;
//determine type
var active = self._getActiveIndex(); // the current active
var direction = self.direction;
var type = direction === 'left' ? 'next' : 'prev';
if (!this.slides[next].classList.contains(type)) {
//e.preventDefault();
//e.defaultPrevented = false;
this.slides[next].classList.add(type);
this.slides[next].offsetWidth;
this.slides[active].classList.add(direction);
this.slides[next].classList.add(direction);
setTimeout(function() {
console.log('inside timeout prevented? ' + e.defaultPrevented);
self.slides[next].classList.remove(type, direction);
self.slides[next].classList.add('active');
self.slides[active].classList.remove('active', direction);
self._curentPage(self.indicators[next]);
//e.defaultPrevented = false;
}, self.duration + 200);
}
},
_getActiveIndex: function() {
return this._getItemIndex('.item.active')
},
_getItemIndex: function(itm) {
return this.slides.indexOf(this.carousel.querySelector(itm))
},
_curentPage: function(p) {
for (var i = 0; i < this.indicators.length; ++i) {
var a = this.indicators[i];
a.className = "";
}
p.className = "active";
},
cycle: function() {
var self = this;
//deleted some shit
},
actions: function() {
var self = this;
self.next.addEventListener("click", function(e) {
e.preventDefault();
self.index++;
self.direction = 'left'; //set direction first
if (self.index == self.total - 1) {
self.index = self.total - 1;
} else if (self.index == self.total) {
self.index = 0
}
self._slideTo(self.index, e);
}, false);
self.prev.addEventListener("click", function(e) {
e.preventDefault();
self.index--;
self.direction = 'right'; //set direction first
if (self.index == 0) {
self.index = 0;
} else if (self.index < 0) {
self.index = self.total - 1
}
self._slideTo(self.index, e);
}, false);
for (var i = 0; i < self.indicators.length; ++i) {
var a = self.indicators[i];
a.addEventListener("click", function(e) {
e.preventDefault();
var n = parseInt(this.getAttribute("data-slide-to"), 10);
self.index = n;
if (self.index == 0) {
self.index = 0;
}
if (self.index > 0) {}
if (self.index == self.total - 1) {
self.index = self.total - 1;
} else {}
//determine direction first
var active = self._getActiveIndex(); // the current active
if ((active < self.index) || (active === self.total - 1 && self.index === 0)) {
self.direction = 'left'; // next
} else if ((active > self.index) || (active === 0 && self.index === self.total - 1)) {
self.direction = 'right'; // prev
}
self._slideTo(self.index, e);
}, false);
}
window.addEventListener('keydown', function(e) {
if (/input|textarea/i.test(e.target.tagName)) return;
switch (e.which) {
case 39:
self.index++;
self.direction = 'left';
if (self.index == self.total - 1) {
self.index = self.total - 1;
} else
if (self.index == self.total) {
self.index = 0
}
break;
case 37:
self.index--;
self.direction = 'right';
if (self.index == 0) {
self.index = 0;
} else
if (self.index < 0) {
self.index = self.total - 1
}
break;
default:
return;
}
// e.preventDefault();
self._slideTo(self.index, e);
}, false)
}
}
var slider = new Carousel("#myCarousel1");
#myCarousel1 {
height: 300px;
max-width: 100%
}
.item {
height: 300px;
background: url(http://placehold.it/100x100/069/069.png) repeat center center;
background-size: cover
}
<link href="http://maxcdn.bootstrapcdn.com/bootstrap/3.3.2/css/bootstrap.min.css" rel="stylesheet" />
<div id="myCarousel1" class="carousel slide" data-ride="carousel">
<ol class="carousel-indicators">
<li data-target="#myCarousel1" data-slide-to="0" class="active"></li>
<li data-target="#myCarousel1" data-slide-to="1"></li>
<li data-target="#myCarousel1" data-slide-to="2"></li>
</ol>
<div class="carousel-inner">
<div class="item active">
<div class="container">
<div class="carousel-caption">
<h1>Example headline.</h1>
<p>Note: If you're viewing this page via a <code>file://</code> URL, the "next" and "previous" Glyphicon buttons on the left and right might not load/display properly due to web browser security rules.</p>
<p><a class="btn btn-lg btn-primary" href="#" role="button">Sign up today</a>
</p>
</div>
</div>
</div>
<div class="item">
<div class="container">
<div class="carousel-caption">
<h1>Another example headline.</h1>
<p>Cras justo odio, dapibus ac facilisis in, egestas eget quam. Donec id elit non mi porta gravida at eget metus. Nullam id dolor id nibh ultricies vehicula ut id elit.</p>
<p><a class="btn btn-lg btn-primary" href="#" role="button">Learn more</a>
</p>
</div>
</div>
</div>
<div class="item">
<div class="container">
<div class="carousel-caption">
<h1>One more for good measure.</h1>
<p>Cras justo odio, dapibus ac facilisis in, egestas eget quam. Donec id elit non mi porta gravida at eget metus. Nullam id dolor id nibh ultricies vehicula ut id elit.</p>
<p><a class="btn btn-lg btn-primary" href="#" role="button">Browse gallery</a>
</p>
</div>
</div>
</div>
</div>
<span class="glyphicon glyphicon-chevron-left"></span>
<span class="glyphicon glyphicon-chevron-right"></span>
</div>
Perhaps checking the time interval between the clicks and returning false from the function that slides the carousel can solve this problem:
var oldTs = 0;
element.removeEventListener("click", slideClickHandler);
function slideClickHandler(e) {
var ts = e.timeStamp;
if ((oldTs !== 0) && (ts - oldTs < 500)) return false; //If time between clicks is 500 miliseconds and its not the first click cancel slide
else {
oldTs = ts; //Update timestamp buffer
slide(); //Do the sliding stuff
}
}
element.addEventListener('click', slideClickHandler);
Edit: You should put this code in a function and refresh the click handler after every slide for it to work.
I need javascript to detect each browser wrapped line of text and wrap it into <span class="line">.
I came across articles talking about measuring the y axis of each word, but have not seen a solid solution.
Here's what I have so far. See it on Jsfiddle.
HTML
<div class="inline-bg">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec cursus condimentum metus, et placerat augue rutrum vitae. Donec arcu lorem, eleifend at elementum eget, consectetur vel lacus. Nam euismod iaculis varius. Phasellus sed dui diam. Nullam facilisis, diam sit amet sagittis cursus, metus tortor gravida erat, ut fringilla risus ipsum eu nisl.</div>
JS/jQuery
(function($){
$.fn.inlinebackground = function() {
$.each(this, function(i,t) {
var split = $(t).html().split(' ');
var output = '';
$.each(split, function(i,o){
output += '<span class="line">'+o+'</span>';
if (i < (split.length - 1)) {
output += '<br>';
}
});
$(t).html(output);
});
}
})(jQuery);
/* End Plugin Code */
// Run the plugin on .news-caption
$(function(){
$('.inline-bg').inlinebackground();
});
CSS
.inline-bg { width: 200px; line-height:3em; }
.inline-bg span.line { background-color: black; color: white; padding: 15px; }
I have to admit at first I thought this would be a daunting task since there is no way to task browser to tell you where auto wrap line breaks take place.
I've created a solution that first wraps each word in a span, then goes through all the spans to determine their top position in container. It then builds an array of indexes of line start and end spans and wraps each line of word spans in a wrapping span.
DEMO: http://jsfiddle.net/KVepp/2/
Possible Limitations:
Space added at end of each span may perhaps cause a span to break to
a new line where text may not.
Not sure if each word span needs to be removed once lines are wrapped. ( very simple mod)
Assumes no other html in container other than text
Needs a little additional work to be turned into a plugin if needed
for multiple containers
regex for words is simple split at space. Likely need additional
regex for recurring spaces
HTML:
<div id="content">Lorem Ipsum<div>
CSS:
#content{ position:relative}
JS:
var $cont = $('#content')
var text_arr = $cont.text().split(' ');
for (i = 0; i < text_arr.length; i++) {
text_arr[i] = '<span>' + text_arr[i] + ' </span>';
}
$cont.html(text_arr.join(''));
$wordSpans = $cont.find('span');
var lineArray = [],
lineIndex = 0,
lineStart = true,
lineEnd = false
$wordSpans.each(function(idx) {
var pos = $(this).position();
var top = pos.top;
if (lineStart) {
lineArray[lineIndex] = [idx];
lineStart = false;
} else {
var $next = $(this).next();
if ($next.length) {
if ($next.position().top > top) {
lineArray[lineIndex].push(idx);
lineIndex++;
lineStart = true
}
} else {
lineArray[lineIndex].push(idx);
}
}
});
for (i = 0; i < lineArray.length; i++) {
var start = lineArray[i][0],
end = lineArray[i][1] + 1;
/* no end value pushed to array if only one word last line*/
if (!end) {
$wordSpans.eq(start).wrap('<span class="line_wrap">')
} else {
$wordSpans.slice(start, end).wrapAll('<span class="line_wrap">');
}
}