jQuery Custom events for data attribute - javascript

I'm building a custom widget system for an app I'm working on. What I want to have happen is the ability for one widget to update another widget by changing out the data attributes value. On page load, the initial data is loaded into this data-attr via PHP and using jQuery to switch out the data after the fact.
For instance, one widget would work as follows:
PHP loads json data into DOM element
jQuery function is passed the elements ID and retrieves data from data-attr and uses it to produce a graph for example
Based on user interaction, another widget sends data to element's data-attr while also firing a custom jQuery event
Initial function gets the new data and updates it's graph
I've started a demo:
// Ranomize Number & Replace Variable
$(function() {
$('#random').click(function(e) {
e.preventDefault();
num = Math.random() + 100;
$('#data').attr('data-receiver', num);
});
});
// Receive Data & Output
$(function() {
var output = $('#output');
var received = $('#data').attr('data-receiver');
output.html(received);
// Not sure what to do next
});
#content {
background: #efefef;
margin: 40px auto;
display: block;
padding: 50px;
width: 50%;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="content">
<div id="data" data-receiver="10"></div>
<strong>Output:</strong>
<span id="output"></span>
<br/>
<br/>
Randomize
</div>
But to be honest I'm not sure how to start. I have some code putting a random value into the receiving DOM element, but not sure how to setup the event or write another function to receive and update the #output div.
I'm happy to answer questions or write more code to help better explain my goal. Thanks in advance.

Try utilizing .queue() , .promise() to create a "subscriber" , "publisher" pattern
var output = $("#output");
var process = function process(next) {
// `this`:`#data`
var num = Math.random() * 100;
$(this).data("receiver", num);
return next()
};
var update = function update() {
// `this`:`#data`
$(this).promise("process").then(function(received) {
// `received`:`#data`,
// do stuff with `received`
console.log(received.data("receiver"));
output.html(received.data("receiver"));
received.queue("process", process);
// add `process` to `received` `"process"` queue
console.log(received, received.queue("process"));
});
};
// queue first call to `process`
$("#data").queue("process", process);
$("#random").click(function (e) {
e.preventDefault();
update.call($("#data").dequeue("process"));
});
jsfiddle http://jsfiddle.net/jev4wuej/2/

I prefer to use custom events which allows for the code to be more decoupe and independent of each other.
jsfiddle
JS
var toolbar = {
init: function() {
$('.data-randomizer').click(this.handleRandomizer);
},
handleRandomizer: function() {
var number = Math.random() + 100;
$.event.trigger('update-request.storage-widget', [number]);
}
};
var output = {
init: function() {
$(document).on('updated.storage-widget', this.handleDisplay);
$.event.trigger('data-request.storage-widget', this.handleDisplay);
},
handleDisplay: function(event, number) {
$('.data-output-widget #output').text(number);
},
requestOut: function() {
}
};
var storage = {
init: function() {
$(document).on('update-request.storage-widget', this.handleUpdateRequest);
$(document).on('data-request.storage-widget', this.handleDataRequest);
},
handleUpdateRequest: function(event, number) {
$('.data-storage-widget').attr('data-receiver', number);
$.event.trigger('updated.storage-widget', [number]);
},
handleDataRequest: function(event, callback) {
var number = $('.data-storage-widget').data('receiver');
callback(event, number);
}
};
toolbar.init();
storage.init();
output.init();
HTML
<div id="content">
<div class="data-storage-widget" data-receiver="10"></div>
<div class="data-output-widget">
<strong>Output:</strong>
<span id="output"></span>
</div>
<div class="tool-bar-widget">
Randomize
</div>
</div>

Related

Best way to hide 10000 dropdown menus

Context -
I have a chat component and each individual chat message has a dropdown.
And the dropdown menu is opened by clicking the "More Options icon"(3 dots).
Each individual chat message is a "backbone item view"
One solution is to listen to click on "body", loop through all the menus and then close the dropdown by removing a class on it.
$("body").on("click", function() {
$(".drop-down-menu").each(function(idx, item) {
$(item).removeClass("open"); // open class indicated it is open via CSS
});
});
The CSS -
.drop-down-menu {
visibility: hidden;
opacity: 0;
&.open {
opacity: 1;
visibility: visible;
}
}
Will there be any performance impact if there are 10,000 messages or more?
Hence, I am looking for the best solution to hide the drop down if user clicks anywhere on the screen.
Thanks.
You can make some trivial changes that should improve the performance of your code. The first thing is that there's no reason to loop like you are doing. jQuery objects are collections and jQuery operations usually loop over the elements of a jQuery object. So:
$("body").on("click", function() {
$(".drop-down-menu").removeClass("open");
});
This will automatically remove the class open from all elements matched by the selector ".drop-down-menu". jQuery will still go over a loop internally, but it is faster to let jQuery iterate by itself than to have .each call your own callback and then inside the callback create a new jQuery object on which to call .removeClass.
Furthermore, you logically know that removing the open class from elements that do not have this class is pointless. So you can narrow the operation to only those elements where removing open makes sense:
$("body").on("click", function() {
$(".drop-down-menu.open").removeClass("open");
});
These are principles that are widely applicable and that have trivial cost to implement. Anything more than this runs into the realm of optimizations that may have downsides, and should be supported by actually profiling your code. You could replace the jQuery code with code that only uses stock DOM calls but then if you need support for old browsers the cost of dealing with this and that quirk may not be worth it. And if you are using stock DOM methods, there are different approaches that may yield different performance increases, at the cost of code complexity.
Louis is offering a quick fix with efficient jQuery selectors.
For the long run, I would suggest making each message a MessageView component which has a ContextMenuView component. That way, each view only has one menu to take care of.
Catching clicks outside of an element
Then, use the following ClickOutside view as the context menu base view. It looks complicated, but it only wraps the blur and focus DOM events to know if you clicked outside the view.
It offers a simple onClickOutside callback for the view itself and a click:outside event which is triggered on the element.
The menu view now only has to implement the following:
var ContextMenuView = ClickOutside.extend({
toggle: function(val) {
this.$el.toggleClass("open", val);
this.focus(); // little requirement
},
// here's where the magic happens!
onClickOutside: function() {
this.$el.removeClass("open");
}
});
See the demo
var app = {};
(function() {
var $body = Backbone.$(document.body);
/**
* Backbone view mixin that enables the view to catch simulated
* "click:outside" events (or simple callback) by tracking the
* mouse and focusing the element.
*
* Additional information: Since the blur event is triggered on a mouse
* button pressed and the click is triggered on mouse button released, the
* blur callback gets called first which then listen for click event on the
* body to trigger the simulated outside click.
*/
var ClickOutside = app.ClickOutside = Backbone.View.extend({
events: {
"mouseleave": "_onMouseLeave",
"mouseenter": "_onMouseEnter",
"blur": "_onBlur",
},
/**
* Overwrite the default constructor to extends events.
*/
constructor: function() {
this.mouseInside = false;
var proto = ClickOutside.prototype;
this.events = _.extend({}, proto.events, this.events);
ClickOutside.__super__.constructor.apply(this, arguments);
this.clickOnceEventName = 'click.once' + this.cid;
},
/**
* Hijack this private method to ensure the element has
* the tabindex attribute and is ready to be used.
*/
_setElement: function(el) {
ClickOutside.__super__._setElement.apply(this, arguments);
var focusEl = this.focusEl;
if (focusEl && !this.$focusElem) {
this.$focusElem = focusEl;
if (!(focusEl instanceof Backbone.$)) {
this.$focusElem = Backbone.$(focusEl);
}
} else {
this.$focusElem = this.$el;
}
this.$focusElem.attr('tabindex', -1);
},
focus: function() {
this.$focusElem.focus();
},
unfocus: function() {
this.$focusElem.blur();
$body.off(this.clickOnceEventName);
},
isMouseInside: function() {
return this.mouseInside;
},
////////////////////////////
// private Event handlers //
////////////////////////////
onClickOutside: _.noop,
_onClickOutside: function(e) {
this.onClickOutside(e);
this.$focusElem.trigger("click:outside", e);
},
_onBlur: function(e) {
var $focusElem = this.$focusElem;
if (!this.isMouseInside() && $focusElem.is(':visible')) {
$body.one(this.clickOnceEventName, this._onClickOutside.bind(this));
} else {
$focusElem.focus(); // refocus on inside click
}
},
_onMouseEnter: function(e) {
this.mouseInside = true;
},
_onMouseLeave: function(e) {
this.mouseInside = false;
},
});
var DropdownView = app.Dropdown = ClickOutside.extend({
toggle: function(val) {
this.$el.toggle(val);
this.focus();
},
onClickOutside: function() {
this.$el.hide();
}
});
})();
var DemoView = Backbone.View.extend({
className: "demo-view",
template: $("#demo-template").html(),
events: {
"click .toggle": "onToggleClick",
},
initialize: function() {
this.dropdown = new app.Dropdown();
},
render: function() {
this.$el.html(this.template);
this.dropdown.setElement(this.$(".dropdown"));
return this;
},
onToggleClick: function() {
this.dropdown.toggle(true);
},
});
$("#app")
.append(new DemoView().render().el)
.append(new DemoView().render().el);
html,
body {
height: 100%;
width: 100%;
}
.demo-view {
position: relative;
margin-bottom: 10px;
}
.dropdown {
z-index: 2;
position: absolute;
top: 100%;
background-color: gray;
padding: 10px;
outline: none;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.3/underscore-min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/backbone.js/1.3.3/backbone-min.js"></script>
<div id="app"></div>
<script type="text/template" id="demo-template">
<button type="button" class="toggle">Toggle</button>
<div class="dropdown" style="display:none;">
This is a drop down menu.
</div>
</script>
Alternatives to detect a click outside an element
If you don't want, or can't use blur and focus events, take a look at How do I detect a click outside an element? for alternative techniques.
Lazy initialization of views
Another way to make an SPA more efficient is to delay the creation of new view to the very moment you need it. Instead a creating 10k context menu views, wait for the first time the user clicks on the toggle button and create a new view if it doesn't exist yet.
toggleMenu: function(){
var menuView = this.menuView;
if (!menuView) {
menuView = this.menuView = new ContextMenuView();
this.$('.dropdown').html(menuView.render().el);
}
menuView.toggle();
}
Pagination
Passed a certain threshold of HTML inside a webpage, the browser starts to lag and it impedes the user experience. Instead of dumping 10k views into a div, only show like a 100, or the minimum to cover the visible space.
Then, when scrolling to an edge (top or bottom), append or prepend new views on demand. Like the message list in any web-based chat app, like messenger.com.
Since you will only have one drop down menu open at a time, maybe you can keep a pointer to the element or index of the element it is attached to, instead of looping through all the menus.

Using checkboxes to update UI in realtime

I'm currently in the process of trying to develop a smarter UI for one of my clients. However the only code I can use to develop this 'feature', is pure JS. I have no access to the source HTML or CSS files the only access I have is the ability to inject JavaScript through an external .js file.
I'm not too familiar with JS, but I can work my way around a basic script or two.
Scenario
What we're doing is allowing users to edit PDF Templates online using a software called Core Create. The UI accessed through the browser is quite cluttered and I would like to provide an option to hide and show UI elements <textareas>/<inputs> through the use of checkboxes.
Here is a very basic JS Fiddle that I have built with the
intention of hiding and displaying UI.
The page in question
Above is a screen grab of the page I am working with, on the left you can see the UI and its composition on the right within the 'Inspect Element' tool.
I have come to the conclusion that I need to iterate through the highlighted selection and link them accordingly with seven checkboxes. The result would then be a selection of checkboxes that would hide / display the correct UI element.
The Caveat
In realizing I cannot edit or introduce new HTML I noticed the lack of on-click attributes. So I'm a bit lost on how to invoke the JavaScript I will eventually build.
My Question
With my limited knowledge of JS I don't know how I would iterate though div elements editoraccvar - editoraccvar6 picking out the ones I need to manipulate.
Due to the lack of ID's / Names (I assume it would have to be done using Parent/Child rules somehow, as the classes are widley used by the rest of the UI. I would appreciate a small example demonstrating how I could achieve this, so I can learn from it.
I should clarify, I have already added the checkboxes to the page, I just need to build the JS link between the Checkbox and the UI element I'm attempting to target. You can find all attributes linking to these checkboxes included in the JS Fiddle.
EDIT // A Working Simplified Example;
Due to some confusion I have 'frankensteined' some code together to show the final result I am after. A working example of sorts. The actual result needs to target 7 Checkboxes and 7 Divisions. I'll list thier common properties below.
// This script is already in place and constructed by the system.
// Written inside script tags and located straight after 'editopt1'.
// $(document).ready(function() {
// $('#checkboxopt1').click(function() {
// if ($('#checkboxopt1').val() == 'true') {
// $('#opt1').val('false');
// $('#checkboxopt1').val('false');
// $('#checkboxopt1').prop('checked', false);
// $('#previewrefresh').trigger('click');
// } else {
// $('#opt1').val('true');
// $('#checkboxopt1').val('true');
// $('#checkboxopt1').prop('checked', true);
// $('#previewrefresh').trigger('click');
// };
// });
// });
function exFunction() {
// Check the function is called
console.log("200 : OK");
// grab all elements with the class, .field-summernote
var uiblocks = document.querySelectorAll('.field-summernote');
for (var i = 0; i < uiblocks.length; i++) {
var current = uiblocks[i];
if (current.className.indexOf('editoraccvar') < 0) //not found: -1
return;
// check elements in the array
console.log(current);
// control the elemets in the array.
if (document.getElementById('checkboxopt1').checked) {
uiblocks[0].style.display = 'block'; // display the element
} else {
uiblocks[0].style.display = 'none'; // hide the element
}
}
};
// Trigger the collection the check, and the control.
var x = document.getElementById("checkboxopt1");
x.addEventListener("click", function() {
console.log("Opt");
exFunction();
});
.editoraccvar1 {
width: 300px;
background: #0ff;
padding: .5em;
}
.editoropt1 {
width: 300px;
background: #ff0;
padding: .5em;
}
textarea {
display: block;
width: 95%;
resize: none;
padding: .5em;
}
<!-- I'm trying to hide & show this entire division... -->
<div class="seq-box-form-field field-summernote editoraccvar1 ">
<label for="accvar1">Ground Floor Info</label>
<div class="clearfix"></div>
<textarea id="richaccvar1" name="richaccvar1" class="summernote"></textarea>
<input type="hidden" name="accvar1" id="accvar1" value="" />
</div>
<!-- Using only what the system has supplied. -->
<div class="seq-box-form-field editoropt1 ">
<label for="opt1"><span style="padding-right: 10px; vertical-align: 1px;">Ground Floor </span>
<input type="checkbox" name="checkboxopt1" id="checkboxopt1" value="true" checked="true" />
<input type="hidden" name="opt1" id="opt1" value="true" />
</label>
</div>
Divisions <div class=""></div>
* editoraccvar,
editoraccvar1,
editoraccvar2,
editoraccvar3,
editoraccvar4,
editoraccvar5,
editoraccvar6*
Checkboxes <input id=""></input>
* checkboxopt,
checkboxopt1,
checkboxopt2,
checkboxopt3,
checkboxopt4,
checkboxopt5,
checkboxopt6,*
As far as I can see, your problem boils down to link checkboxes (that seem to have been generated in some way) to "division" parts of your html that you want to hide. Plus, you have to inject javascript code in the page (so I guess the less code the better).
One approach could be as follows:
// Wrap the code in an anonymus function, to avoid clustering the global space.
(function (domElements) {
// This is the callback that will fire when a checkbox is clicked.
function clickCallback() {
// the context of this callback is the DOM element thus we can access its attributes through this.
// extract the checkNumber of the class of the element. This number is the link to the division that we want to hide/show.
var checkNumber = ((/ editoropt(\d*) /).exec(this.className))[1],
checkBox = document.getElementById('checkboxopt' + checkNumber),
division = document.querySelectorAll('.editoraccvar' + checkNumber)[0];
// Hide/show division, update checkBox state.
toggleElements(division, checkBox, window.getComputedStyle(division).display === 'none');
}
function toggleElements(division, checkBox, isShown) {
// Toggle the division (show/hide) accordingly.
division.style.display = isShown ? 'block' : 'none';
// Due to the fact that the event listener is attached to the parent of the checkBox, we need to maintain consistency manually.
checkBox.checked = isShown;
}
// Remove from the array of DOMElements those that aren't checkboxes and add a click event listener to each of them.
domElements
.filter(function (el) {
return el.className.indexOf('editoropt') !== -1;
})
.forEach(function (el) {
el.addEventListener('click', clickCallback, false);
});
// Call the function passing the dom elements with class '.seq-box-form-field' as argument. Checkboxes are contained within them. Also, transform the nodelist
// into a proper array so that methods defined in Array.prototype can be used.
})([].slice.call(document.querySelectorAll('.seq-box-form-field')));
The code is commented and (I think) quite self-explanatory. However, if you have any doubt or want me to elaborate any point further, please, let me know.
Finally, here's the working fiddle.
UPDATE
Same function (more or less) but now it accepts an array of values that will correspond to the initial state of the checkboxes:
(function (domElements, cbState) {
function clickCallback() {
toggleElements(this.className);
}
function toggleElements(className, initialShow) {
var checkNumber = ((/ editoropt(\d*) /).exec(className))[1],
checkBox = document.getElementById('checkboxopt' + checkNumber),
division = document.querySelectorAll('.editoraccvar' + checkNumber)[0],
isShown = initialShow === undefined ? window.getComputedStyle(division).display === 'none' : initialShow;
division.style.display = isShown ? 'block' : 'none';
checkBox.checked = isShown;
}
domElements
.filter(function (el) {
return el.className.indexOf('editoropt') !== -1;
})
.forEach(function (el, index) {
el.addEventListener('click', clickCallback, false);
toggleElements(el.className, cbState[index]);
});
// Initial state of the checkboxes goes in the second parameter. The index in the array correspond to the checkbox position in the page.
})([].slice.call(document.querySelectorAll('.seq-box-form-field')), [false, false]);
Here's the Fiddle to play with. Hope it helps.
The other half of your problem, not addressed in the other answer has to do with events. Generally, adding an "onclick" attribute to the actual HTML is considered bad practice. You can attach event handlers with Javascript.
var a = document.getElementById("checkboxopt1");
a.addEventListener("click", exFunction, false);
See the manual for more info about how to use this.
Looks like that you need the elements that have the class "field-summernote", but not the class "editorbdyvar".
You can use a query selector to get elements by class name using the default tools from Javascript:
var items = document.querySelectorAll('.field-summernote');
for(var i = 0; i<items.length; i++){
var current = items[i];
if( current.className.indexOf('editoraccvar') < 0) //not found: -1
return;
//now you can manipulate the current element
console.log(current);
}
well ... you should either learn javascript, DOM, HTML and CSS or hire an somebody that can do it.
in my opinion the latter would come cheaper.
if not,
here goes something to put in your script.js file.
the checkboxes must have the id="toggleTextareas" respectively id="toggleInputs".
(function isolateScope() {
tryInit();
function tryInit() {
if(document.readyState!="complete"){
setTimeout(tryInit, 100);
}else{
createUI();
init();
}
}
function createUI(){
var div=document.createElement("div");
div.className="addon-floating-toolbar"
div.style.position="fixed";
div.style.zIndex="999999";
div.style.background="#EEE";
div.style.padding="5px";
div.innerHTML='<input type="checkbox" id="toggleTextareas">toggle Textareas<br>'
+'<input type="checkbox" id="toggleInputs">toggle Inputs';
document.body.appendChild(div);
}
function init() {
var tta=document.getElementById("toggleTextareas");
var ti=document.getElementById("toggleInputs");
var textareaVisible=true;
var inputVisible=true;
tta.onclick=toggleTextareas;
ti.onclick=toggleInputs;
function toggleTextareas() {
var elms=document.querySelectorAll("textarea");
textareaVisible=!textareaVisible;
if (textareaVisible) {
show(elms);
}else{
hide(elms);
}
}
function toggleInputs() {
var elms=document.querySelectorAll("input");
inputVisible=!inputVisible;
if (inputVisible) {
show(elms);
}else{
hide(elms);
}
}
function show(collection) {
for (var i = 0; i < collection.length; i++) {
collection[i].style.display="";
}
}
function hide(collection) {
for (var i = 0; i < collection.length; i++) {
collection[i].style.display="none";
}
}
}
})();
let me know if it works,
cheers.
You can traverse all your fields and generate a checkbox that will toggle it open/close for each of your fields. Also set the checkbox label as innerText of the corresponding field.
// Block to be run
generateCheckboxes = function() {
var button = document.getElementById("generateButton");
button.parentNode.removeChild(button);
// grab all elements with the class, .field-summernote
var uiblocks = [].slice.call(document.querySelectorAll('.field-summernote')).filter(function(x) {
return x.className.indexOf('editoraccvar') >= 0
});
if (!uiblocks.length) return;
var chcontainer = document.createElement('div');
chcontainer.style.display = "inline-block";
document.body.insertBefore(chcontainer, document.body.children[0]);
uiblocks.forEach(function(x) {
var cdiv = document.createElement('div');
var clabel = document.createElement('label');
clabel.innerHTML = x.innerText.trim();
var cinput = document.createElement('input');
cinput.type = 'checkbox';
cinput.checked = true;
cinput.onchange = function(ev) {
var checked = this.checked;
x.style.display = checked ? "" : "none";
}
cdiv.appendChild(clabel);
cdiv.appendChild(cinput);
cdiv.appendChild(document.createElement('br'));
chcontainer.appendChild(cdiv);
})
};
#container {
width: 150px;
}
input {
float: left;
}
label {
width: 120px;
display: block;
float: right;
text-align: left;
}
<button onclick="generateCheckboxes()" id="generateButton">Generate Checkboxes</button>
<div id="example" class="field-summernote editoraccvar">
<br/>
<br/>
<span>Zero</span>
<br/>
<textarea></textarea>
</div>
<div id="example1" class="field-summernote editoraccvar1">
<br/>
<br/>
<span>One</span>
<br/>
<textarea></textarea>
</div>
<div id="example2" class="field-summernote">
<br/>
<br/>
<span>Two</span>
<br/>
<textarea></textarea>
</div>
Fiddle

Search for word and highlight with jquery

I have written a javaScript file in jQuery that provides a search function. I am trying to figure out how to highlight the word aswell. Bellow is the code.
Filter.js:
(function ($) {
// custom css expression for a case-insensitive contains()
jQuery.expr[":"].Contains = jQuery.expr.createPseudo(function(arg) {
return function( elem ) {
return jQuery(elem).text().toUpperCase().indexOf(arg.toUpperCase()) >= 0;
};
});
function listFilter(header, list, title) {
// header is any element, list is an unordered list, title is any element
// create and add the filter form to the header
// create a button for collapse/expand to the title
var form = $("<form>").attr({"class":"filterform","action":"#"}),
button = $("<input>").attr({"class":"rest", "type":"submit", "value":"Collapse All", "id":"switch"}),
input = $("<input>").attr({"class":"filterinput","type":"text", "placeholder":"Search"});
$(form).append(input).appendTo(header); //add form to header
$(title).append(button); //add button to title
//on click function for collapse/expand all
$("#switch").click(function(){
if($(this).val() == "Collapse All"){
$(".filterinput").val("");
$(this).val("Expand All");
$("div.content div.markdown").parent().parentsUntil(list).hide();
$(list).find("span.path").parentsUntil(list).show();
$(list).find("ul.endpoints").css("display", "none");
}
else{
$(".filterinput").val("");
$(this).val("Collapse All");
$("div.content div.markdown").parent().parentsUntil(list).hide();
$(list).find("span.path").parentsUntil(list).show();
}
});
$(input)
.change( function () {
var filter = $(this).val();
if(filter) {
// this finds a single string literal in div.markdown,
// and hides the ones not containing the input while showing the ones that do
$(list).find("div.content div.markdown:not(:Contains(" + filter + "))").parent().parentsUntil(list).hide();
$(list).find("div.content div.markdown:Contains(" + filter + ")").parent().parentsUntil(list).show();
}
else {
$("div.content div.markdown").parent().parentsUntil(list).hide();
$(list).find("span.path").parentsUntil(list).show();
$(list).find("ul.endpoints").css("display", "none");
}
return false;
})
.keyup( function () {
// fire the above change event after every letter
$(this).change();
});
}
//ondomready
setTimeout(function () {
listFilter($("#header"), $("#resources"), $("#api_info"));
}, 250);
}(jQuery));
The html that I would like to manipulate is being dynamically created by another JS file so I need to manipulate the DOM after it has been completely rendered.. The html that I will be focusing on gets rendered as bellow, specifially the words in (div class="markdown").
Index.html:
<div class="content" id="connectivitypacks_get_connectivitypacks_content">
<h4>Description</h4>
<div class="markdown"><p>Response will return details for the connectivity packs based on the ID.</p>
<h2 id="keywords">Keywords</h2>
<p> foo, bar, helloWorld, java</p>
</div>
</div>
Here is an example that used your markdown.
Create a regex with that word your searching for.
Get the html of your .markdown
replace the word with <span class="marker">"+ word +"</span>. So this creates a span tag around the word your searching for.
Create css to style the word as needed.
function highlight(word) {
var element = $('.markdown');
var rgxp = new RegExp(word, 'g');
var repl = '<span class="marker">' + word + '</span>';
element.html(element.html().replace(word, repl));
}
highlight('details');
.marker {
background-color: yellow;
font-weight: bold;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="content" id="connectivitypacks_get_connectivitypacks_content">
<h4>Description</h4>
<div class="markdown">
<p>Response will return details for the connectivity packs based on the ID.</p>
<h2 id="keywords">Keywords</h2>
<p>foo, bar, helloWorld, java</p>
</div>
</div>
Have a look at mark.js. It can highlight such search terms in a specific context. In your example the JavaScript would look like:
var searchTerm = $("#theInput").val();
// Search for the search term in your context
$("div.markdown").mark(searchTerm, {
"element": "span",
"className": "highlight"
});
and the CSS part:
span.highlight{
background: yellow;
}

Get numerical value from parent with id like 'post-1' and use it in jQuery function

I'm trying to figure out the following.
I have following jQuery code:
var as = "";
var bPlay = 0;
audiojs.events.ready(function() {
as = audiojs.createAll();
$(".audiojs .play-pause").click(function() {
var e = $(this).parents(".audiojs").index(".audiojs");
$.each(as, function(t, n) {
if (t != e && as[t].playing) {
as[t].pause()
}
})
bPlay = !bPlay;
if (bPlay == 1) {
$(".bar").each(function(i) {
fluctuate($(this));
});
} else {
$(".bar").stop();
}
})
});
In a nutshell it preforms list of things when someone clicks particular .audiojs instance on a page. 1) checks if there is any other instance playing, if there is pauses it. And if it is playing applies fluctuate function to elements on a page that have class="bar". This is the issue! I don't want to apply it to all .bar's on a page, but only to a specific group that is associated with particular .audiojs instance (the one that is being clicked and is playing).
I thought of the following solution. Each .audiojs instance is inside a div tag that has id like "post-1", "post-2" etc.. where numerical value is post id from database. I can add this numerical id to bar, so it would be like bar-1, bar-2 etc... However after this I'm having issues.
For javascript to work I need to retrieve numerical value from "post-[id]" associated with audiojs instance that is being clicked and than store it somehow, so I can use it like this afterwards
bPlay = !bPlay;
if (bPlay == 1) {
$(".bar-[value retrieved from post-...]").each(function(i) {
fluctuate($(this));
});
} else {
$(".bar-[value retrieved from post...]").stop();
}
Could someone explain to me how it can be achieved?
Honestly, the easiest way would be to stick it in a custom data-* attribute on the <div id="post-X"> element, like so:
<div id="post-1" data-bar="bar-1">...</div>
Then, you said your .audiojs element is inside that <div>, so just go from this inside the event handler to that <div> element (using .closest()) and get the value of it:
var barId = $(this).closest('[id^="post-"]').attr('data-bar');
Then when you need to use it:
$("." + barId).each(function(i) {
fluctuate($(this));
});
Instead of embedding the value in a class or ID, use a data-* attribute:
<div class="audiojs" data-fluctuate-target="bar-1">
<button type="button" class="play-pause">
<!-- ... -->
</button>
</div>
<div class="bar-1">
<!-- ... -->
</div>
In your click event handler, use the following to fluctuate or stop the correct elements:
var fluctuateClass = $(this).closest('.audiojs').attr('data-fluctuate-target');
$('.' + fluctuateClass).each(function () {
if (bPlay == 1) {
fluctuate($(this));
} else {
$(this).stop();
}
});

Running continuous events after each other

I've been trying to make this for 2 days, I apologize I'm new to Javascript/Jquery and I'm in the learning process.
I'm trying to create a javascript when the page is loaded will have an image fade in and fade out and after the first image fades in and out then a second, third, etc. however many I need.
I know this is a newby question but I'm clearly not sure what to look for at this point. And I have been doing research and learning along the way, I just would like to have it sooner than I may be able to accomplish.
Any help is appreciated.
This is what I came up with which to me looks completely invalid, but seems to work:
<div class="splashbg1" style="display: none;"></div>
<div class="splashbg2" style="display: none;"></div>
<div class="splashbg3" style="display: none;"></div>
<div class="splashbg4" style="display: none;"></div>
<div class="splashbg5" style="display: none;"></div>
<script>
$(document).ready(function() {
$('.splashbg1').fadeIn(1300, function() {
$('.splashbg1').fadeOut(1300, function() {
$('.splashbg2').fadeIn(1300, function() {
$('.splashbg2').fadeOut(1300, function() {
$('.splashbg3').fadeIn(1300, function() {
$('.splashbg3').fadeOut(1300, function() {
$('.splashbg4').fadeIn(1300, function() {
$('.splashbg4').fadeOut(1300, function() {
$('.splashbg5').fadeIn(1300, function() {
});
});
});
});
});
});
});
});
});
});
</script>
example HTML:
<div id="images>
<img src="">
<img src="">
...
</div>
example Javascript:
function switchImage(){
$('#images img:visible').fadeOut(function(){
$(this).next().length ? $(this).next().fadeIn() : $('#images img').eq(0).fadeIn();
});
};
$('#images img').hide().eq(0).show(); // show only the first image
setInterval(switchImage, 2000); // loop through images every 2000 milliseconds
example: http://jsfiddle.net/ampersand/nhp2v/
You can use an array and a variable containing the current image, then fade them in and out. It depends on how your images are stored. If you have one <img> element with an ID of myImg, for example, you could do this:
var images = ['http://someurl.com/someimg.jpg', 'somethingelse.png', 'hello.gif'];
var currentImage = 0;
function next() {
$('#myImg').fadeOut(function() {
$('#myImg').prop('src', images[currentImage]).fadeIn(); // Set the image and fade in
currentImage++; // Get ready for the next image
});
}
next();
If you wanted it to wrap, you could do images[currentImage % images.length]. If there are a bunch of different images, you can do about the same thing, just keep the IDs of the images in images instead.
I wrote a quick function called sequence below. Make a list of the images you'd like to animate using jQuery selectors like this:
var list = [
$('first'),
$('second'),
$('third')
];
Then call the function below with something like this sequence( list, 200, function(){} );
var sequence = function( array, duration, callback ){
var list = array,
length = list.length,
i = 0,
duration = duration,
callback = callback;
function chainFade(){
if( i < length )
array[i].fadeIn( duration )
.fadeOut( duration, function(){
i++;
chainFade();
});
else
typeof callback == 'function' && callback();
}
chainFade();
};
I haven't tested it yet so let me know if you run into any bugs.

Categories

Resources