返回
解决多组幻灯片按钮失效问题:JS 完美修复
javascript
2025-03-07 10:25:20
<h1>让你的多组幻灯片按钮动起来!</h1>
<h2>问题</h2>
<p>
你是不是遇到过这样的情况:页面上有几组幻灯片,每组都有自己的“上一个”和“下一个”按钮,但是只有第一组的按钮能正常工作? 点击其他组的按钮,啥反应都没有,页面一动不动。 这让人很头疼!
</p>
<h2>原因分析</h2>
<p>
问题很可能出在 JavaScript 代码里用来选择按钮的 CSS 选择器上。原来的代码可能只选中了第一组幻灯片的按钮,忽略了其他的。
</p>
<h2>解决方案</h2>
<p>
想要搞定这个问题,咱们得让代码找到<em>所有</em>的按钮,并给它们都加上事件监听器。下面是几个可以尝试的方法:
</p>
<h3>方法一: 使用 querySelectorAll 和循环</h3>
<p>
这是个比较直接的方法。咱们用 `querySelectorAll` 把所有符合条件的按钮都选出来,然后用一个循环,给每个按钮都加上点击事件的监听。
</p>
<p><strong>原理:</strong></p>
<ul>
<li><code>querySelectorAll('.action-buttons .up-button')</code>: 这行代码找到页面上所有带有 "action-buttons" 类的元素,然后在这些元素里面寻找带有 "up-button" 类的子元素,也就是我们的向上按钮。</li>
<li><code>querySelectorAll('.action-buttons .down-button')</code>: 这行代码做了类似的事情,只不过这次找的是向下按钮(带有 "down-button" 类的元素)。</li>
<li><code>forEach(button => { ... })</code>: 对选取出来的所有按钮,逐一进行同样的处理。 <code>button</code>代表每一个被循环到的按钮.</li>
<li><code>button.addEventListener('click', () => changeSlide('up'))</code> : 给按钮加个监听,一旦按钮被点("click" 事件发生),就运行 <code>changeSlide('up')</code> 函数。</li>
</ul>
<p><strong>代码示例:</strong></p>
```javascript
const sliderContainers = document.querySelectorAll('.slider-container');
sliderContainers.forEach(container => {
const slideRight = container.querySelector('.right-slide');
const slideLeft = container.querySelector('.left-slide');
const upButton = container.querySelector('.up-button');
const downButton = container.querySelector('.down-button');
const slidesLength = slideRight.querySelectorAll('div').length;
let activeSlideIndex = 0;
slideLeft.style.top = `-${(slidesLength - 1) * 100}vh`;
upButton.addEventListener('click', () => changeSlide(container, 'up', slidesLength));
downButton.addEventListener('click', () => changeSlide(container, 'down', slidesLength));
});
function changeSlide(container, direction, slidesLength) {
const sliderHeight = container.clientHeight;
let activeSlideIndex = parseInt(container.dataset.activeSlideIndex || 0); //从container里面获取 index.
if (direction === 'up') {
activeSlideIndex++;
if (activeSlideIndex > slidesLength - 1) {
activeSlideIndex = 0;
}
} else {
activeSlideIndex--;
if (activeSlideIndex < 0) {
activeSlideIndex = slidesLength - 1;
}
}
const slideRight = container.querySelector('.right-slide');
const slideLeft = container.querySelector('.left-slide');
slideRight.style.transform = `translateY(-${sliderHeight * activeSlideIndex}px)`;
slideLeft.style.transform = `translateY(${sliderHeight * activeSlideIndex}px)`;
container.dataset.activeSlideIndex = activeSlideIndex; // 把最新的index存回去.
}
//以下代码无需修改.
window.kontext = function(container) {
// Dispatched when the current layer changes
var changed = new kontext.Signal();
// All layers in this instance of kontext
var layers = Array.prototype.slice.call(container.querySelectorAll('.layer'));
// Flag if the browser is capable of handling our fancy transition
var capable = 'WebkitPerspective' in document.body.style ||
'MozPerspective' in document.body.style ||
'msPerspective' in document.body.style ||
'OPerspective' in document.body.style ||
'perspective' in document.body.style;
if (capable) {
container.classList.add('capable');
}
// Create dimmer elements to fade out preceding slides
layers.forEach(function(el, i) {
if (!el.querySelector('.dimmer')) el.innerHTML += '<div class="dimmer"></div>';
});
/**
* Transitions to and shows the target layer.
*
* @param target index of layer or layer DOM element
*/
function show(target, direction) {
// Make sure our listing of available layers is up to date
layers = Array.prototype.slice.call(container.querySelectorAll('.layer'));
// Flag to CSS that we're ready to animate transitions
container.classList.add('animate');
// Flag which direction
direction = direction || (target > getIndex() ? 'right' : 'left');
// Accept multiple types of targets
if (typeof target === 'string') target = parseInt(target);
if (typeof target !== 'number') target = getIndex(target);
// Enforce index bounds
target = Math.max(Math.min(target, layers.length), 0);
// Only navigate if were able to locate the target
if (layers[target] && !layers[target].classList.contains('show')) {
layers.forEach(function(el, i) {
el.classList.remove('left', 'right');
el.classList.add(direction);
if (el.classList.contains('show')) {
el.classList.remove('show');
el.classList.add('hide');
} else {
el.classList.remove('hide');
}
});
layers[target].classList.add('show');
changed.dispatch(layers[target], target);
}
}
/**
* Shows the previous layer.
*/
function prev() {
var index = getIndex() - 1;
show(index >= 0 ? index : layers.length + index, 'left');
}
/**
* Shows the next layer.
*/
function next() {
show((getIndex() + 1) % layers.length, 'right');
}
/**
* Retrieves the index of the current slide.
*
* @param of [optional] layer DOM element which index is
* to be returned
*/
function getIndex(of) {
var index = 0;
layers.forEach(function(layer, i) {
if ((of && of == layer) || (!of && layer.classList.contains('show'))) {
index = i;
return;
}
});
return index;
}
/**
* Retrieves the total number of layers.
*/
function getTotal() {
return layers.length;
}
// API
return {
show: show,
prev: prev,
next: next,
getIndex: getIndex,
getTotal: getTotal,
changed: changed
};
};
/**
* Minimal utility for dispatching signals (events).
*/
kontext.Signal = function() {
this.listeners = [];
}
kontext.Signal.prototype.add = function(callback) {
this.listeners.push(callback);
}
kontext.Signal.prototype.remove = function(callback) {
var i = this.listeners.indexOf(callback);
if (i >= 0) this.listeners.splice(i, 1);
}
kontext.Signal.prototype.dispatch = function() {
var args = Array.prototype.slice.call(arguments);
this.listeners.forEach(function(f, i) {
f.apply(null, args);
});
}
// Create a new instance of kontext
var k = kontext(document.querySelector('.kontext'));
// Demo page JS
var bulletsContainer = document.body.querySelector('.bullets');
// Create one bullet per layer
for (var i = 0, len = k.getTotal(); i < len; i++) {
var bullet = document.createElement('li');
bullet.className = i === 0 ? 'active' : '';
bullet.setAttribute('index', i);
bullet.onclick = function(event) {
k.show(event.target.getAttribute('index'))
};
bullet.ontouchstart = function(event) {
k.show(event.target.getAttribute('index'))
};
bulletsContainer.appendChild(bullet);
}
// Update the bullets when the layer changes
k.changed.add(function(layer, index) {
var bullets = document.body.querySelectorAll('.bullets li');
for (var i = 0, len = bullets.length; i < len; i++) {
bullets[i].className = i === index ? 'active' : '';
}
});
document.addEventListener('keyup', function(event) {
if (event.keyCode === 37) k.prev();
if (event.keyCode === 39) k.next();
}, false);
进阶用法:
为了让每个slide的内容不一样, 可以把 slidesLength 等信息存放在 `slider-container`的`dataset`里面。
方法二:事件委托
事件委托是个更巧妙的方法。我们可以把事件监听器加在所有按钮的共同父元素上,然后通过事件对象 (event) 来判断是哪个按钮被点击了。
原理:
- 事件会“冒泡”,也就是说,子元素上的事件会被传递到父元素。
- 我们可以利用这一点,把事件监听器加在父元素上,减少监听器的数量。
- 用 `event.target`找到实际被点击的目标.
代码示例 (这种做法在这个案例里需要对 HTML 结构有修改,所以先不展示):
由于你的 HTML 的按钮分别被各自的`slider-container`包裹, 使用事件委托比较麻烦, 反而不简洁。 方法一更好。
安全建议
- 这段代码控制的是前端元素的行为, 没有直接和用户数据交互,所以没有直接的安全问题
这些方法都能解决你的问题!选一个你觉得最顺眼的就好。如果代码还是跑不通,或者你有别的问题,随时可以再问我!
```