intro.js show and hide data-hint - javascript

I want to have a button that can turn on and off the 'hints' function in intro.js.
I have a working version to show and then hide but the show only works once. How can I get it to work repeatedly? This functionality works for the standard data-intro but not for data-hint.
<div class="jumbotron">
<h1 id='step1'>Hints</h1>
<p class="lead">Adding hints using JSON + callbacks</p>
<a id='step2' class="btn btn-large btn-success" href="javascript:void(0);">Add hints</a>
</div>
function addHints(){
intro = introJs();
intro.setOptions({
hints: [
{
element: document.querySelector('#step1'),
hint: "This is a tooltip.",
hintPosition: 'top-middle'
},
{
element: '#step2',
hint: 'More features, more fun.',
position: 'left'
},
{
element: '#step4',
hint: "<b>Another</b> step.",
hintPosition: 'top-middle'
}
]
});
intro.onhintsadded(function() {
console.log('all hints added');
});
intro.onhintclick(function(hintElement, item, stepId) {
console.log('hint clicked', hintElement, item, stepId);
});
intro.onhintclose(function (stepId) {
console.log('hint closed', stepId);
});
intro.addHints();
}
$(function() {
$('#step2').click(function(){
if ( $('#step2').hasClass('clicked') ) {
introJs().hideHints();
$('#step2').removeClass('clicked');
} else {
addHints();
$('#step2').addClass('clicked');
}
});
});

Instead of using hideHints intro.js API method just remove the div block of intro.js from DOM:
var introDiv = document.getElementsByClassName("introjs-hints")[0];
introDiv.parentNode.removeChild(introDiv);
(You can do the same thing with jQuery if you want to).
When the div is removed from DOM, just initialize hints once again as you do with your addHints method when you want to show hints and it'll work.

Instead of deleting the div block with javascript. You can use .removeHints()
This function is part of intro.js, but is not included in the documentation.

Perhaps a bit hacky, but this works for me...
First, put your hints into their own variable:
hints = [{...}, ...]
then, reset your hints in the intro options
intro.onhintclose(function(stepId) {
if (document.querySelectorAll('.introjs-hidehint').length === hints.length) {
intro.setOptions({hints: hints})
}
})
The hidden hints are given a class of introjs-hidehint, and document.querySelectorAll will return all of them in an array. Once that array is the same size as your hints array, reset your hints in your intro options and that will reset all your hints so you can show them all again.

Here's a more complete example that also allows:
(a) toggling hints on/off by clicking a button (located on a nav bar so used across multiple pages).
(b) once all hints have been clicked, the hints div gets removed so that clicking show hints button will again actually...show hints...
(c) allow you to store hints for multiple pages in a single json object array (re: nav bar).
var jquery = require('jquery');
var introJs = require('intro.js');
* ===========================================================================
* define onclick of hints button
* =========================================================================*/
jquery('#hints_button').on('click', function() {
if (document.getElementsByClassName('introjs-hints').length == 0){
addSomeHints();
}
else {
destroyHints();
};
});
/* ===========================================================================
* Add hints using the IntroJS library
* =========================================================================*/
/* define hints */
var theHints = [
{
element: document.querySelector('#step1'),
hint: "This is a tooltip.",
hintPosition: 'top-middle'
},
{
element: '#step2',
hint: 'More features, more fun.',
hintPosition: 'left'
},
{
element: '#step4',
hint: "<b>Another</b> step.",
hintPosition: 'top-middle'
}
];
/* generate hints with introjs */
function addSomeHints() {
intro = introJs();
intro.setOptions({
hints: theHints
});
intro.onhintclose(function (stepId) {
var remaining_hints = all_hints - document.getElementsByClassName("introjs-hidehint").length;
if (remaining_hints == 0) {
destroyHints();
};
});
/* add hints */
intro.addHints();
/* store number of hints created */
var all_hints = document.getElementsByClassName('introjs-hint').length;
};
/* remove hints div */
function destroyHints() {
var hintsDiv = document.getElementsByClassName("introjs-hints")[0]
hintsDiv.parentNode.removeChild(hintsDiv);
};
... hopefully this saves someone the 20 minutes it took me to piece together the answers and adapt it for what seems like a super common use case.

Related

Monaco Editor: only show part of document

Is there a way to only show part of a document, or in monacos case of a model, while still getting intellisense for the whole document?
I only want a user to edit a part of a document, but the user should be able to get the right contextual intellisense.
It would be best for my usecase to hide the uneditable sections, but deactivating them would also be ok.
In case this is not possible, is there any embedded editor that can do this, or can this be achived by modifying the language server?
Monaco editor loads every line as a container under a section with the class name "view-lines". Once the editor content has loaded, set "display: none" to the corresponding container for each line that you want to hide.
Implementation: https://jsfiddle.net/renatodc/s6fxedo2/
let value = `function capitalizeFirstLetter(string) {
\treturn string.charAt(0).toUpperCase() + string.slice(1);
}
$(function() {
\tlet word = "script";
\tlet result = capitalizeFirstLetter(word);
\tconsole.log(result);
});
`
let linesToDisable = [1,2,3];
let editor = monaco.editor.create(document.getElementById('container'), {
value,
language: 'javascript',
theme: 'vs-dark',
scrollbar: {
vertical: "hidden",
handleMouseWheel: false
},
scrollBeyondLastLine: false
});
// onLoad event for Monaco Editor: https://github.com/Microsoft/monaco-editor/issues/115
let didScrollChangeDisposable = editor.onDidScrollChange(function() {
didScrollChangeDisposable.dispose();
setTimeout(function() {
$(".monaco-editor .view-lines > div").each(function(i) {
if(linesToDisable.includes(i+1)) {
$(this).css("display","none");
$(this).css("pointer-events","none");
}
});
},1000);
});
Scrolling from Monaco will render the lines again and break the implementation. To prevent this, disable the scrolling feature in Monaco, set a fixed height for the editor container, and use the browser or a parent container to scroll instead.
If you use the arrow keys 'up' or 'down' to navigate to the hidden content, the cursor will still work, and typing will break the implementation. You might be able to use the editor's onKeyDown event to prevent this.
If you're looking for a break-proof implementation, I would suggest loading Monaco editor only with the portion of the document that you wish to edit. Then extend the completion provider (Intellisense) as shown in this example: https://microsoft.github.io/monaco-editor/playground.html#extending-language-services-completion-provider-example
monaco.languages.registerCompletionItemProvider('javascript', {
provideCompletionItems: function(model, position) {
return {
suggestions: [
{
label: "capitalizeFirstLetter",
kind: monaco.languages.CompletionItemKind.Method,
documentation: "Capitalize the first letter of a word",
insertText: "capitalizeFirstLetter("
}
]
};
}
});
monaco.editor.create(document.getElementById("container"), {
value: `$(function() {
\tlet word = "script";
\tlet result = capitalizeFirstLetter(word);
\tconsole.log(result);
});
`,
language: "javascript"
});
Use an AST parser like Esprima to get the identifiers from your source document, and plug these into the suggestions array.

bootstrap + mixitup filter on load issue

We are trying to show our portfolio in bootstrap via MixItUp v3.1.11 filtering and are trying to load a certain category (not all the projects) when the page is loaded.
Patrick Kunka has provided an example how it could be done here.
Same problem was asked here
Our issue is that the reccomended soultion is not working. My guess is that it is related to the change of selector due to bootstrap + mixitup issues:
control: '[data-mixitup-control]'
Here is the piece of code that is at the end of the page:
var containerEl = document.querySelector('#selector');
var mixer = mixitup(containerEl, {
selectors: {
target: '.mix',
control: '[data-mixitup-control]'
},
animation: {
effects: 'fade scale stagger(50ms)' // Set a 'stagger' effect for the loading animation
},
load: {
filter: 'none' // Ensure all targets start from hidden (i.e. display: none;)
}
});
// Add a class to the container to remove 'visibility: hidden;' from targets. This
// prevents any flickr of content before the page's JavaScript has loaded.
containerEl.classList.add('mixitup-ready');
// Show all targets in the container
mixer.show()
.then(function() {
// Remove the stagger effect for any subsequent operations
mixer.configure({
animation: {
effects: 'fade scale'
},
load: {
filter: '.residential' // Ensure all targets start from hidden (i.e. display: none;)
}
});
});
When I change the filter to desired .residential it does not work.
I also tried to add this:
$(function(){
$('#selector').mixItUp({
load: {
filter: '.residential'
}
});
});
No luck. Any idea anyone where could the problem be?
In combination with MixItUp v3.1.9 and Bootstrap 4 try this example.
Add data-mixitup-control to avoid conflicts with other data-attributes:
<button type="button" class="control" data-mixitup-control data-`enter code here`filter=".product">PRODUCTS</button>
Initialize MixItUp:
var container = document.querySelector('.portfolio');
var mixer = mixitup(container, {
selectors: {
control: '[data-mixitup-control]'
}
});
I've tried the example, and it works with version 2.1.6.
Here is my example code:
$(function(){
$('#selector').mixItUp({
selectors: {
target: '.item'
},
layout: {
display: 'inline-block'
},
load: {
filter: '.residential'
}
});
});
This will load the DOM only with items related to .residential visible.
Hope this helps.
In combination with MixItUp Version 3.1.11 and Bootstrap 3 try this example.
Add data-mixitup-control to avoid conflicts with other data-attributes:
<button type="button" class="control" data-mixitup-control data-filter=".residential">Residential</button>
Initialize MixItUp:
var containerEl = document.querySelector('.container');
var mixer = mixitup(containerEl, {
selectors: {
control: '[data-mixitup-control]'
},
load: {
filter: '.residential'
}
});
You don't need to set the 'fade scale' animation effect separately - it's the default setting. https://www.kunkalabs.com/mixitup/docs/configuration-object/

Tinymce 3 - insert new content in new block after custom button click

I want to ask if it's possible to add new content "outside" of content that has beed added recently.
So, i have custom button which adds some simple HTML.
And what i want to archive is to add the same html but outside of existing one, so in place marked green on my screenshot. I'm looking for a way how to escape from this div, and add new html after existing one.
below screenshot, and code - how javascript button in generated - very simple.
Thanks for advice.
var oferta = '<div class="col-sm-3"><h1>test</h1></div>'
setup: function (ed) {
ed.addButton('example', {
title: 'example.desc',
image: './/',
text: 'Oferta',
icon: true,
onclick: function () {
tinyMCE.execCommand('mceInsertContent', false, oferta);
}
});
},
EDIT: Below how this looks now when i hit button 3 times in row.(every next content is added to existing one.)
Is very easy to do it try to change you code with this example.
setup: function (editor) {
ed.addButton('example', {
title: 'example.desc',
image: './/',
text: 'Oferta',
icon: true,
onclick: function () {
var h1 = editor.dom.create('h1');
h1.innerText = 'test';
var oferta = editor.dom.create('div' ,{'class': 'col-sm-3'});
oferta.appendChild(h1);
var divs = editor.dom.select('div');
if(divs && divs.length > 0){
editor.dom.insertAfter(oferta,divs[divs.length-1])
}else{
tinyMCE.execCommand('mceInsertContent', false,oferta.outerHTML);
}
editor.selection.select(oferta);
editor.selection.collapse(true);
}
});
},

Disable the whole jsTree

I'm using jsTree and have a form on the right of it based on the selected node that can be edited/saved. The goal is to prevent the user from clicking anywhere else on the tree while they are editing the form.
Is there any way to temp disable/enable the tree functionality while still keeping the tree visually available?
I tried using the disable_node(obj) method and apply it to the root of the tree but doesn't seem to be a solution.
Any suggestions? Or this is not a possible feature for the jsTree lib?
Thanks
To disable selected node do it this way:
var node = $("#tree").jstree().get_selected();
$("#tree").jstree().disable_node(node);
To disable all nodes use:
$('#tree li').each( function() {
$("#tree").jstree().disable_node(this.id);
})
UPDATED
I didn't find a way to prevent opening a disabled node so I'm just disabling all the children of a closed node too.
See demo: Fiddle
Edit As pointed out by #charles disabling the nodes doesn't disable the menu plugin (at least with a custom menu) or drag'n'drop - Added point 4 to take care of this
To disable the whole tree
Disable all rendered nodes - disable each node by id or get an array of ids to do a single call to "disable_node"
Forbid the opening of new nodes - intercept and block the click event on the open icon
Forbid the opening of new nodes with double click - modify the current tree settings
If the tree is user modifiable in any way, temporary disable all modifications setting core.check_callback = false
Note The point 2 is base on undocumented features and (given the history of the jstree plugin) will probably not work on future releases
See the snippet for a demo
var data1 = [{
"id": "W",
"text": "World",
"state": { "opened": true },
"children": [{"text": "Asia"},
{"text": "Africa"},
{"text": "Europe",
"state": { "opened": false },
"children": [ "France","Germany","UK" ]
}]
}];
$('#Tree').jstree({
core: {data: data1,
check_callback: true
},
plugins: ['dnd','contextmenu','checkbox']
})
function DisableFlawed() {
// this is not enough
$('#Tree li.jstree-node').each(function() {
$('#Tree').jstree("disable_node", this.id)
})
}
function Disable() {
// disable visible nodes
$('#Tree li.jstree-node').each(function() {
$('#Tree').jstree("disable_node", this.id)
})
// block open new nodes
$('#Tree i.jstree-ocl')
.off('click.block')
.on('click.block', function() {
return false;
});
// eventually... dbl click
$('#Tree').jstree().settings.core.dblclick_toggle = false;
// eventually... block all edits
$('#Tree').jstree().settings.core.check_callback = false;
}
function Enable() {
// enable again visible nodes
$('#Tree li.jstree-node').each(function() {
$('#Tree').jstree("enable_node", this.id)
});
// ublock open new nodes
$('#Tree i.jstree-ocl')
//
.off('click.block');
// eventually... dbl click
$('#Tree').jstree().settings.core.dblclick_toggle = true;
// eventually... unblock all edits
// set to true OR reset to whatever user defined function you are using
$('#Tree').jstree().settings.core.check_callback = true;
}
<link href="https://cdnjs.cloudflare.com/ajax/libs/jstree/3.2.1/themes/default/style.min.css" type="text/css" rel="stylesheet" />
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jstree/3.2.1/jstree.min.js"></script>
<button onclick="DisableFlawed()">Disable (bad)</button>
<button onclick="Disable()">Disable (ok)</button>
<button onclick="Enable()">Enable</button>
<div id="Tree"></div>
How about this?
// get an instance of jstree.
var tree = $.jstree.create('#tree', { ... });
// disable all nodes with one line.
tree.disable_node(tree.get_json(null, { flat: true }));
In addition to Nikolay Ermakov's answer, disabling the nodes doesn't disable the menu plugin (at least with a custom menu) or drag'n'drop. If you want to do so, you need to add an extra check in these functions (tested with JsTree 3.2.1)
$('#tree').jstree({
// ...
contextmenu: {
items: customMenu
},
dnd: {
is_draggable: function (node) {
return !node[0].state.disabled;
}
},
});
function customMenu(node)
{
if (node.state.disabled)
return false;
// usual menu generation code
}
Another way is to use something jQuery BlockUI plugin to do some general blocking outside of jsTree.

Comparison between Javascript objects

I have made a simple accordion for my site using jQuery... It worked great, but I've recently started working on a change where if you click the currently opened segments title (the clickable area to slide up/down), it should close the current section.
var sideMenu = {
activated: {},
setup: function() {
$('.menu-category-heading').bind('click', function() {
sideMenu.slideMenu($('ul', $(this).parent()));
});
},
slideMenu: function(menuObj) {
if (sideMenu.activated == menuObj) {
$(sideMenu.activated).slideUp(400);
sideMenu.activated = null;
console.log('same');
} else {
$(sideMenu.activated).slideUp(400);
menuObj.slideDown(500);
sideMenu.activated = menuObj;
console.log('new');
}
}
}
For some reason the comparison is never working... it does if I add $(menuObj).attr('id') and the same for activated. But this is not ideal as not all items will have an id attribute.
Any suggestions as to make the object comparison work? Or any other tips?
Thank you!
You are probably saving a jQuery object (the result of a $ call) rather than the native element. Each time you do a $(myEl) a new object is created and the references will not match up, but the native element will. Try:
if (slideMenu.activated[0] == menuObj[0]) {
...
}

Categories