I'm trying to implement mark.js on a page, but it isn't working correctly. So I setup a very basic page, and pulled all of the code from this jsfiddle page, however it will only highlight certain 1-3 letters at a time, depending on whatever I put in. Can anyone see what I'm doing wrong exactly? My page is located here.
Code:
$(function() {
// the input field
var $input = $("input[type='search']"),
// clear button
$clearBtn = $("button[data-search='clear']"),
// prev button
$prevBtn = $("button[data-search='prev']"),
// next button
$nextBtn = $("button[data-search='next']"),
// the context where to search
$content = $(".content"),
// jQuery object to save <mark> elements
$results,
// the class that will be appended to the current
// focused element
currentClass = "current",
// top offset for the jump (the search bar)
offsetTop = 50,
// the current index of the focused element
currentIndex = 0;
/**
* Jumps to the element matching the currentIndex
*/
function jumpTo() {
if ($results.length) {
var position,
$current = $results.eq(currentIndex);
$results.removeClass(currentClass);
if ($current.length) {
$current.addClass(currentClass);
position = $current.offset().top - offsetTop;
window.scrollTo(0, position);
}
}
}
/**
* Searches for the entered keyword in the
* specified context on input
*/
$input.on("input", function() {
var searchVal = this.value;
$content.unmark({
done: function() {
$content.mark(searchVal, {
separateWordSearch: true,
done: function() {
$results = $content.find("mark");
currentIndex = 0;
jumpTo();
}
});
}
});
});
/**
* Clears the search
*/
$clearBtn.on("click", function() {
$content.unmark();
$input.val("").focus();
});
/**
* Next and previous search jump to
*/
$nextBtn.add($prevBtn).on("click", function() {
if ($results.length) {
currentIndex += $(this).is($prevBtn) ? -1 : 1;
if (currentIndex < 0) {
currentIndex = $results.length - 1;
}
if (currentIndex > $results.length - 1) {
currentIndex = 0;
}
jumpTo();
}
});
});
This ended up being an encoding issue. Adding:
<meta charset="UTF-8">
resolved the problem.
Related
I have this snippet found below which highlights and jumps to the searched term. The current snippet searches after each keystroke that the user inputs which is putting too much stress on the server. Instead I want it to mark and jump once the user presses enter or clicks the next button. I've tried change the following line but it's breaking the code. Any ideas?
$input.on("input", function() {
to
$nextBtn.on('click', function() {
Code here:
$(function() {
// the input field
var $input = $("input[type='search']"),
// clear button
$clearBtn = $("button[data-search='clear']"),
// prev button
$prevBtn = $("button[data-search='prev']"),
// next button
$nextBtn = $("button[data-search='next']"),
// the context where to search
$content = $(".content"),
// jQuery object to save <mark> elements
$results,
// the class that will be appended to the current
// focused element
currentClass = "current",
// top offset for the jump (the search bar)
offsetTop = 50,
// the current index of the focused element
currentIndex = 0;
/**
* Jumps to the element matching the currentIndex
*/
function jumpTo() {
if ($results.length) {
var position,
$current = $results.eq(currentIndex);
$results.removeClass(currentClass);
if ($current.length) {
$current.addClass(currentClass);
position = $current.offset().top - offsetTop;
window.scrollTo(0, position);
}
}
}
/**
* Searches for the entered keyword in the
* specified context on input
*/
$input.on("input", function() {
var searchVal = this.value;
$content.unmark({
done: function() {
$content.mark(searchVal, {
separateWordSearch: true,
done: function() {
$results = $content.find("mark");
currentIndex = 0;
jumpTo();
}
});
}
});
});
/**
* Clears the search
*/
$clearBtn.on("click", function() {
$content.unmark();
$input.val("").focus();
});
/**
* Next and previous search jump to
*/
$nextBtn.add($prevBtn).on("click", function() {
if ($results.length) {
currentIndex += $(this).is($prevBtn) ? -1 : 1;
if (currentIndex < 0) {
currentIndex = $results.length - 1;
}
if (currentIndex > $results.length - 1) {
currentIndex = 0;
}
jumpTo();
}
});
});
Working JSFiddle found here: https://jsfiddle.net/83nbm2rv/
You can change the $input.on('input') to:
$input.on("keypress", function(e) {
if (e.which === 13) {
var searchVal = this.value;
$content.unmark({
done: function() {
$content.mark(searchVal, {
separateWordSearch: true,
done: function() {
$results = $content.find("mark");
currentIndex = 0;
jumpTo();
}
});
}
});
}
});
And that will handle pressing enter in the textbox. See this fiddle for the next button click update: https://jsfiddle.net/9g4xr765/
Basic approach was to functionalize the content marking and calling it on $input keypress, and also in next/previous click if there are no results.
There are still issues though, like if the value changes you can't use the next/previous button to search so that would require some additional work.
I want to trigger an event to execute code wrapped in $('.selector').on('event').
The selector change just before I trigger the event.
To understand the problem : I have a text on page 1 and the function trigger a click to go to the page 2 (this works btw!). So now I'm on page 2 and I want to trigger a click to execute code in on() method. And this doesn't work.
I've tried many things and nothing works, the only way to execute the code in on() method is to manually click on the selector.
This is the entire code actually :
jQuery(function() {
// the input field
$input = jQuery("input[type=\'search\']");
// clear button
var $clearBtn = jQuery("button[data-search=\'clear\']"),
// prev button
$prevBtn = jQuery("button[data-search=\'prev\']"),
// next button
$nextBtn = jQuery("button[data-search=\'next\']"),
// the context where to search
$content = jQuery(".search_content"),
// jQuery object to save <mark> elements
$results,
// the class that will be appended to the current
// focused element
currentClass = "current",
// top offset for the jump (the search bar)
offsetTop = 50,
// the current index of the focused element
currentIndex = 0,
//the current ajaxpage
currentPage = 1;
/**
* Jumps to the element matching the currentIndex
*/
function jumpTo() {
if ($results.length) {
var position,
$current = $results.eq(currentIndex);
$results.removeClass(currentClass);
if ($current.length) {
$current.addClass(currentClass);
position = $current.offset().top - offsetTop - 100;
window.scrollTo(0, position);
}
}
}
var mark = function() {
// Read the keyword
var keyword = $input.val();
// empty options
var options = {};
// Remove previous marked elements and mark
// the new keyword inside the content
jQuery(".search_content").unmark({
done: function() {
jQuery(".search_content").mark(keyword, options);
$results = $content.find("mark");
currentIndex = 0;
jumpTo();
}
});
};
function registerClear() {
$input.on("input", function() {
searchVal = this.value;
$content.unmark({
done: function() {
$content.mark(searchVal, {
separateWordSearch: true,
done: function() {
$results = $content.find("mark");
currentIndex = 0;
console.log(searchVal);
console.log("page2");
jumpTo();
}
});
}
});
});
}
registerClear();
/**
* Clears the search
*/
$clearBtn.on("click", function() {
$content.unmark();
$input.val("").focus();
});
/**
* Next and previous search jump to
*/
$nextBtn.add($prevBtn).on("click", function() {
if ($results.length) {
currentIndex += jQuery(this).is($prevBtn) ? -1 : 1;
if (currentIndex < 0) {
currentIndex = $results.length - 1;
}
if (currentIndex > $results.length - 1) {
currentIndex = 0;
}
//TODO : - LINK DONE - SEARCH ON EACH PAGE IF THERE ARE OCCURENCE
if (currentIndex == 0 && jQuery(this).is($nextBtn)) {
if (confirm("No more instances found! Go to the next page?")) {
alert("NEXT PAGE");
jQuery("a[data-page=\'2\']").click();
jQuery(document).ready(function() {
jQuery(".search_content").click();
});
jQuery(document.body).on("click", ".search_content", mark)
registerClear();
} else {
//do nothing
}
}
jumpTo();
}
});
});
this is the important part:
if (currentIndex == 0 && jQuery(this).is($nextBtn)) {
if (confirm("No more instances found! Go to the next page?")) {
alert("NEXT PAGE");
jQuery("a[data-page=\'2\']").click();
jQuery(document).ready(function() {
jQuery(".search_content").click();
});
jQuery(document.body).on("click", ".search_content", mark)
registerClear();
} else {
//do nothing
}
}
What I've tried :
I've search many things on SO.
Since the content on which I trigger the event is loaded via ajax I thought it was because I triggered the event to soon, so I've tried wrapping my code in document.ready, or with setTimeOut function. No results.
I've tried this :
jQuery('#bar')[0].click();
This :
jQuery(document).ready(function(){
jQuery('#foo').on('click', function(){
jQuery('#bar').simulateClick('click');
});
});
jQuery.fn.simulateClick = function() {
return this.each(function() {
if('createEvent' in document) {
var doc = this.ownerDocument,
evt = doc.createEvent('MouseEvents');
evt.initMouseEvent('click', true, true, doc.defaultView, 1, 0, 0, 0, 0, false, false, false, false, 0, null);
this.dispatchEvent(evt);
} else {
this.click(); // IE Boss!
}
});
}
I've tried putting the trigger after the on() method.
I've tried changing the element on which I do the event.
And also changing the event triggered.
Now I feel like I've tried everything, so I am asking for your help please.
$('element_selector').trigger('eventname', param1, param2,...);
this will trigger given event, params are optional if required
I don't know what's wrong with my code. I cannot pass a function in my javascript, [i don't want to put it inline]
My problem is my prev button and next button doesn't work, I also tried to put return false on prev and next to stop refreshing the page, but it still refreshing on click.
This is my code [please also see my comments] and my codepen:
$(document).ready(function slider() {
$('#img1').show('fade', 500);
$('#img1').delay(5000).hide("slide", { direction: 'left' }, 500);
});
var count = 2;
setInterval(function loop() {
var all = document.getElementsByTagName('li').length; // <-- i got the li elements so i did the same to prev and next
$('#img' + count).show('slide', { direction: 'right' }, 500);
$('#img' + count).delay(5500).hide('slide', { direction: 'left' }, 500);
if (count === all) {
count = 1;
} else {
count += 1;
}
}, 6500);
var sliderInt = 1;
var sliderNext = 2;
document.getElementsByClassName('prev').onclick = function prev() { // <-- not working
console.log('clicked prev');
var newSlide = sliderInt - 1;
showSlide(newSlide);
return false;
}
document.getElementsByClassName('next').onclick = function next() { // <-- not working
console.log('clicked next');
var newSlide = sliderInt + 1;
showSlide(newSlide);
return false;
}
function stopLoop() {
window.clearInterval(loop());
}
function showSlide(id) { // <-- this function doesn't work from prev and next
stopLoop(); // <-- I want to stop the loop() function when prev and next is clicked
if (id > count) {
id = 1;
} else if (id < 1) {
id = count;
}
$('li').hide('slide', { direction: 'left' }, 500);
$('#img' + id).show('slide', { direction: 'right' }, 500);
sliderInt = id;
sliderNext = id + 1;
window.slider(); // <-- I want to call the function slider here
}
a fix demo will be much appreciated :)
When you use the document.getElementsByClassName('prev').onclick you got an array. Use it like below
document.getElementsByClassName('prev')[0].onclick
document.getElementsByClassName('next')[0].onclick
getElementsByClassName returns a HTMLCollection. So you need to pass the relevant index to which you want to add the onclick function
document.getElementsByClassName('next')[0]
But this will attach the event only on the first element in the collection.
An more relevant example is
var list = document.getElementsByClassName('next or prev');
for (var i = 0, len = list.length; i < len; i++) {
(function(i){ // creating closure
list[i].addEventListener('click',function(){
// code you want to execute on click of next or prev
}
}(i))
}
As you are already using jquery you can avoid all this if you use class selector
$('.next or .prev').on('click',function(event){
// relevant code
})
This is the markup for my navigation:
<div class="navigation navigation-fixed-top">
Home
About
</div>
And I have this jquery script, which is checking if href="#home" has class active to do something and if not to do something else.
This is the code:
var isActive = $('a[href="#home"]').hasClass('active');
$(".navigation")
.toggleClass("navigation-fixed-bottom", isActive)
.toggleClass("navigation-fixed-top", !isActive);
This is partially working because the class="active" is added automatically when I'm going the #about section or I'm clicking on it. It does this without refreshing the page so I need a way to to make this work without refreshing the page.
Any suggestions on how can I do this with jQuery/Javascript ?
UPDATE:
this is the name of the plugin Scrollit.js
THIS IS THE CODE RESPONSIBLE FOR ADDING THE ACTIVE CLASS ON THE NAVIGATION ELEMENTS:
(function($) {
'use strict';
var pluginName = 'ScrollIt',
pluginVersion = '1.0.3';
/*
* OPTIONS
*/
var defaults = {
upKey: 38,
downKey: 40,
easing: 'linear',
scrollTime: 600,
activeClass: 'active',
onPageChange: null,
topOffset : 0
};
$.scrollIt = function(options) {
/*
* DECLARATIONS
*/
var settings = $.extend(defaults, options),
active = 0,
lastIndex = $('[data-scroll-index]:last').attr('data-scroll-index');
/*
* METHODS
*/
/**
* navigate
*
* sets up navigation animation
*/
var navigate = function(ndx) {
if(ndx < 0 || ndx > lastIndex) return;
var targetTop = $('[data-scroll-index=' + ndx + ']').offset().top + settings.topOffset + 1;
$('html,body').animate({
scrollTop: targetTop,
easing: settings.easing
}, settings.scrollTime);
};
/**
* doScroll
*
* runs navigation() when criteria are met
*/
var doScroll = function (e) {
var target = $(e.target).closest("[data-scroll-nav]").attr('data-scroll-nav') ||
$(e.target).closest("[data-scroll-goto]").attr('data-scroll-goto');
navigate(parseInt(target));
};
/**
* keyNavigation
*
* sets up keyboard navigation behavior
*/
var keyNavigation = function (e) {
var key = e.which;
if(key == settings.upKey && active > 0) {
navigate(parseInt(active) - 1);
return false;
} else if(key == settings.downKey && active < lastIndex) {
navigate(parseInt(active) + 1);
return false;
}
return true;
};
/**
* updateActive
*
* sets the currently active item
*/
var updateActive = function(ndx) {
if(settings.onPageChange && ndx && (active != ndx)) settings.onPageChange(ndx);
active = ndx;
$('[data-scroll-nav]').removeClass(settings.activeClass);
$('[data-scroll-nav=' + ndx + ']').addClass(settings.activeClass);
};
/**
* watchActive
*
* watches currently active item and updates accordingly
*/
function navPosition() {
$('[data-scroll-nav]').toggleClass('navigation-fixed-bottom navigation-fixed-top');
}
var updateActive = function(ndx, navPosition) {
var watchActive = function() {
var winTop = $(window).scrollTop();
var visible = $('[data-scroll-index]').filter(function(ndx, div) {
return winTop >= $(div).offset().top + settings.topOffset &&
winTop < $(div).offset().top + (settings.topOffset) + $(div).outerHeight()
});
var newActive = visible.first().attr('data-scroll-index');
updateActive(newActive);
};
/*
* runs methods
*/
$(window).on('scroll',watchActive).on('scroll');
$(window).on('keydown', keyNavigation);
$('body').on('click','[data-scroll-nav], [data-scroll-goto]', function(e){
e.preventDefault();
doScroll(e);
});
};
}(jQuery));
$('[data-scroll-nav]').removeClass(settings.activeClass)
.toggleClass('navigation-fixed-bottom navigation-fixed-top');
and/or
$('[data-scroll-nav=' + ndx + ']').addClass(settings.activeClass)
.toggleClass('navigation-fixed-bottom navigation-fixed-top');
If you wanted to keep the original code cleaner, you could do a callback:
function myCallBackFunction() {
$('[data-scroll-nav]').toggleClass('navigation-fixed-bottom navigation-fixed-top');
}
var updateActive = function(ndx, myCallbackFunction) {..}
use jquery on to bind handlers to events you want to respond
$("a.active").on('click',function(){});
The code (from an old plugin that I am trying to make responsive) slides a set of images across every n seconds. It uses setInterval code as below, and works well on Firefox. On Chrome it runs once only, and debugging indicates that the second setInteral function is just not called. Please help as its diving me mad. Running example at http://lelal.com/test/site10/index.html (sorry about the load time)
play = setInterval(function() {
if (!busy) {
busy = true;
updateCurrent(settings.direction);
slide();
}
}, settings.speed);
The complete plugin code is below (sorry its long)
/*
* jQuery Queue Slider v1.0
* http://danielkorte.com
*
* Free to use and abuse under the MIT license.
* http://www.opensource.org/licenses/mit-license.php
*/
(function($){
var QueueSlider = function(element, options) {
var play = false,
busy = false,
current = 2,
previous = 2,
widths = [],
slider = $(element),
queue = $('ul.queue', slider),
numImages = $('img', queue).size(),
viewportWidth = slider.width(),
settings = $.extend({}, $.fn.queueSlider.defaults, options);
$(window).resize(function(){
if(busy !== false)
clearTimeout(busy);
busy = setTimeout(resizewindow, 200); //200 is time in miliseconds
});
function resizewindow() {
viewportWidth = slider.width();
if (settings.scale > 0) {
slider.css('height',viewportWidth * settings.scale);
computeQueueWidth();
}
queue.css('left', -getQueuePosition());
busy = false;
}
function requeue() {
$('li', queue).each(function(key, value) {
$(this).attr('class', 'slide-' + (key+1));
});
}
function updateCurrent(dir) {
current += dir;
if (current < 1) {
current = numImages;
} else if (current > numImages) {
current = 1;
}
}
function getQueuePosition() {
var i = 0, index = current-1,
queuePosition = (viewportWidth - widths[index]) / -2;
for (i = 0; i < index; i++) { queuePosition += widths[i]; }
return queuePosition;
}
function computeQueueWidth() {
var queueWidth = 0;
// factor = slider.height() / settings.imageheight;
// settings.imageheight = settings.imageheight * factor;
// Get the image widths and set the queue width to their combined value.
$('li', queue).each(function(key, value) {
var slideimg = $("img", this),
slide = $(this),
// width = slide.width() * factor,
width = slideimg.width();
slide.css('width', width+'px');
queueWidth += widths[key] = width;
});
queue.css('width', queueWidth + 500);
}
function slide() {
var animationSettings = {
duration: settings.transitionSpeed,
queue: false
};
// Emulate an infinte loop:
// Bring the first image to the end.
if (current === numImages) {
var firstImage = $('li.slide-1', queue);
widths.push(widths.shift());
queue.css('left', queue.position().left + firstImage.width()).append(firstImage);
requeue();
current--; previous--;
}
// Bring the last image to the beginning.
else if (current === 1) {
var lastImage = $('li:last-child', queue);
widths.unshift(widths.pop());
queue.css('left', queue.position().left + -lastImage.width()).prepend(lastImage);
requeue();
current = 2; previous = 3;
}
// Fade in the current and out the previous images.
if (settings.fade !== -1) {
$('li.slide-'+current, queue).animate({opacity: 1}, animationSettings);
$('li.slide-'+previous, queue).animate({opacity: settings.fade}, animationSettings);
}
// Animate the queue.
animationSettings.complete = function() { busy = false; };
queue.animate({ left: -getQueuePosition() }, animationSettings);
previous = current;
}
//
// Setup the QueueSlider!
//
if (numImages > 2) {
// Move the last slide to the beginning of the queue so there is an image
// on both sides of the current image.
if (settings.scale > 0) {
slider.css('height',viewportWidth * settings.scale);
}
computeQueueWidth();
widths.unshift(widths.pop());
queue.css('left', -getQueuePosition()).prepend($('li:last-child', queue));
requeue();
// Fade out the images we aren't viewing.
if (settings.fade !== -1) { $('li', queue).not('.slide-2').css('opacity', settings.fade); }
// Include the buttons if enabled and assign a click event to them.
if (settings.buttons) {
slider.append('<button class="previous" rel="-1">' + settings.previous + '</button><button class="next" rel="1">' + settings.next + '</button>');
$('button', slider).click(function() {
if (!busy) {
busy = true;
updateCurrent(parseInt($(this).attr('rel'), 10));
clearInterval(play);
slide();
}
return false;
});
}
// Start the slideshow if it is enabled.
if (settings.speed !== 0) {
play = setInterval(function() {
if (!busy) {
busy = true;
updateCurrent(settings.direction);
slide();
}
}, settings.speed);
}
}
else {
// There isn't enough images for the QueueSlider!
// Let's disable the required CSS and show all one or two images ;)
slider.removeClass('queueslider');
}
};
$.fn.queueSlider = function(options) {
return this.each(function(key, value) {
var element = $(this);
// Return early if this element already has a plugin instance.
if (element.data('queueslider')) { return element.data('queueslider'); }
// Pass options to plugin constructor.
var queueslider = new QueueSlider(this, options);
// Store plugin object in this element's data.
element.data('queueslider', queueslider);
});
};
$.fn.queueSlider.defaults = {
scale: 0,
imageheight: 500,
fade: 0.3, // Opacity of images not being viewed, use -1 to disable
transitionSpeed: 700, // in milliseconds, speed for fade and slide motion
speed: 7000, // in milliseconds, use 0 to disable slideshow
direction: 1, // 1 for images to slide to the left, -1 to silde to the right during slideshow
buttons: true, // Display Previous/Next buttons
previous: 'Previous', // Previous button text
next: 'Next' // Next button text
};
}(jQuery));
Have a look here:
http://www.w3schools.com/jsref/met_win_setinterval.asp
The setInterval() method will continue calling the function until clearInterval() is called, or the window is closed.
Looks like you're calling clearInterval after the first usage of play, which makes it stop working.