How can the Leaflet JS layer control be closed using JS code? On desktop, the control closes nicely when the mouse cursor leaves the control. However, on mobile phones, the user needs to tap outside the control to close it. I would like to manually close it once a user selects a layer inside the control.
The state of this control is controlled by the leaflet-control-layers-expanded class. If you add or remove this class to the leaflet-control-layers element, then you can control the state.
These examples use jQuery for simplicity.
To expand the control:
$(".leaflet-control-layers").addClass("leaflet-control-layers-expanded")
To collapse the control:
$(".leaflet-control-layers").removeClass("leaflet-control-layers-expanded")
For mobile devices, I would simply add a close button to the div and then use js to change the class as mentioned above:
Note that I changed the leaflet source code here but it should be feasible externally as well. Add the following code before the line container.appendChild(form); in your leaflet source - tested with 0.7.7)
if (L.Browser.android || L.Browser.mobile || L.Browser.touch || L.Browser.retina) {
var yourCloseButton = this.yourCloseButton = L.DomUtil.create('div', className + '-close');
this.yourCloseButton = L.DomUtil.create('div', className + '-close', form);
this.yourCloseButton.innerHTML = '<button class="btn-close-layers-control">X</button>';
L.DomEvent.on(this.yourCloseButton, 'click', this._collapse, this);
}
`Then position the button with css.
Related
I've been trying out the excellent Medium Editor. The problem that I've been having is that I can't seem to get links to "work".
At the simplest, here's some HTML/JS to use to demonstrate the problem:
HTML:
<html>
<head>
<script src="//cdn.jsdelivr.net/medium-editor/latest/js/medium-editor.min.js"></script>
<link rel="stylesheet" href="//cdn.jsdelivr.net/medium-editor/latest/css/medium-editor.min.css" type="text/css" media="screen" charset="utf-8">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/medium-editor/latest/css/themes/beagle.min.css" type="text/css">
</head>
<body>
<div class='editable'>
Hello world. link
</div>
</body>
</html>
Javascript:
var editor = new MediumEditor('.editable');
This fiddle demonstrates the problem (using the code above).
If you hover on the link, a popup appears.
If you click the link, nothing happens.
If you click the popup, a form appears where you can edit the link.
It seems to me that clicking the link should take me wherever the link's href is targeting. The only way to use the link is to right click and either open in a new tab or new window -- which I don't want to ask my users to do.
I feel like I must be missing something simple in the configuration (either the Anchor Preview Options or the Anchor Form Options). Unfortunately, I'm not seeing it.
In my actual application, I'm not using jQuery, but I am using angularjs. If a strictly Medium Editor answer doesn't exist, I can fall back to using basic JS or anything that angularjs provides.
I've found how to bind event.
Here is full event list https://github.com/yabwe/medium-editor/blob/master/CUSTOM-EVENTS.md
Try to change your code to
var editor = new MediumEditor('.editable')
.subscribe("editableClick", function(e){if (e.target.href) {window.open(e.target.href)}})
https://jsfiddle.net/fhr18gm1/
So medium-editor is built on top of the built-in browser support for contenteditable elements. When you instantiate medium-editor, it will add the contenteditable=true attribute to whatever element(s) you provided it.
By default, since the text is now editable (the contenteditable attribute makes the browser treat it as WYSIWYG text) the browser no longer supports clicking on the links to navigate. So, medium-editor is not blocking these link clicks from happening, the browsers do it inherently as part of making the text editable.
medium-editor has built in extensions for interacting with links:
anchor extension
allows for adding/removing links
anchor-preview extension
shows a tooltip when hovering a link
when the tooltip is clicked, allows for editing the href of the link via the anchor extension
I think the underlying goal of the editor is the misunderstanding here. The editor allows for editing text, and in order to add/remove/update links, you need to be able to click into it without automatically navigating away. This is what I think of as 'edit' mode.
However, the html produced as a result of editing is valid html, and if you take that html and put it inside an element that does NOT have the contenteditable=true attribute, everything will work as expected. I think of this as 'publish mode'
I look at editors like word or google docs, and you see a similar kind of behavior where when you edit the document, the links don't just navigate away when you click on them, you have to actually choose to navigate them through a separate action after you click the link. However, on a 'published' version of the document, clicking the link will actually open a browser window and navigate there.
I think this does make for a good suggestion as an enhancement to the existing anchor-preview extension. Perhaps the tooltip that appears when hovering a link could have multiple options in it (ie Edit Link | Remove Link | Navigate to URL).
tldr;
Links are not navigable on click when 'editing' text in a browser via the built-in WYSIWYG support (contenteditable). When not in 'edit' mode, the links will work as expected.
This could make for a nice enhancement to the medium-editor anchor-preview extension.
Working off some ideas from #Valijon in the comments, I was able to get it to work using the following code:
var iElement = angular.element(mediumEditorElement);
iElement.on('click', function(event) {
if (
event.target && event.target.tagName == 'A' &&
event.target.href && !event.defaultPrevented) {
$window.open(event.target.href, '_blank');
}
});
I think the key is that apparently the editor lets the event propogate to the ancestor elements, so I was able to just listen for the click on the top level editor element.
Here, $window is angular's $window service -- If you're not using angularjs, window would do the trick and I used angular.element to ease the event listener registry, but you could do it the old-fashioned way (or using the JS framework of your choice).
What I really wanted when I asked the question was behavior similar to Google Docs when in "edit" mode (as described by Nate Mielnik). I opened an issue on the Medium Editor tracker and they decided not to implement it as part of the core medium editor, but they noted that they would be happy to have someone add that functionality as an extension.
So, I decided to implement that functionality as an extension as suggested. It can be found as part of MediumTools1. The project is still in very early stages (e.g. I haven't done anything to make the styling look better, or to use better minifying practices, etc. but we'll happily accept Pull Requests for that).
The guts of the code look like this:
var ClassName = {
INNER: 'medium-editor-toolbar-anchor-preview-inner',
INNER_CHANGE: 'medium-editor-toolbar-anchor-preview-inner-change',
INNER_REMOVE: 'medium-editor-toolbar-anchor-preview-inner-remove'
}
var AnchorPreview = MediumEditor.extensions.anchorPreview;
GdocMediumAnchorPreview = MediumEditor.Extension.extend.call(
AnchorPreview, {
/** #override */
getTemplate: function () {
return '<div class="medium-editor-toolbar-anchor-preview">' +
' <a class="' + ClassName.INNER + '"></a>' +
' -' +
' <a class="' + ClassName.INNER_CHANGE + '">Change</a>' +
' |' +
' <a class="' + ClassName.INNER_REMOVE + '">Remove</a>' +
'</div>';
},
/** #override */
createPreview: function () {
var el = this.document.createElement('div');
el.id = 'medium-editor-anchor-preview-' + this.getEditorId();
el.className = 'medium-editor-anchor-preview';
el.innerHTML = this.getTemplate();
var targetBlank =
this.getEditorOption('targetBlank') ||
this.getEditorOption('gdocAnchorTargetBlank');
if (targetBlank) {
el.querySelector('.' + ClassName.INNER).target = '_blank';
}
var changeEl = el.querySelector('.' + ClassName.INNER_CHANGE);
this.on(changeEl, 'click', this.handleClick.bind(this));
var unlinkEl = el.querySelector('.' + ClassName.INNER_REMOVE);
this.on(unlinkEl, 'click', this.handleUnlink.bind(this));
return el;
},
/** Unlink the currently active anchor. */
handleUnlink: function() {
var activeAnchor = this.activeAnchor;
if (activeAnchor) {
this.activeAnchor.outerHTML = this.activeAnchor.innerHTML;
this.hidePreview();
}
}
});
As an explanation, I just use medium's flavor of prototypical inheritance to "subclass" the original/builtin AnchorPreview extension. I override the getTemplate method to add the additional links into the markup. Then I borrowed a lot from the base implementation of getPreview, but I bound new actions to each of the links as appropriate. Finally, I needed to have an action for "unlinking" the link when "Remove" is clicked, so I added a method for that. The unlink method could probably be done a little better using contenteditable magic (to make sure that it is part of the browser's undo stack), but I didn't spend the time to figure that out (though it would make a good Pull Request for anyone interested :-).
1Currently, it's the only part, but I hope that'll change at some point. . .
I am desperately trying to dynamically add buttons into leaflet.js marker popups and assign a callback. However, I seem not to be able to wrap my head around it.
I am using this example https://github.com/calendee/ionic-leafletjs-map-demo and add the following line into line 105 in js/controller/mapController.js
<button class="icon-left ion-information" ng-click="stationInfoButtonClick('+location.name+')"></button>
However, clicking / taping the button does not invoke the specified callback function. Ideas somebody?
You are trying to add HTML to AngularJS Code. This is not possible just like that.
You need to use $compileProvider for that.
On a previous version of leaflet, I managed to do that by calling the $compileProvider on popUp open.
$scope.$on('leafletDirectiveMap.popupopen', function(event, args) {
var feature = args.leafletEvent.popup.options.feature;
var newScope = scope.$new();
newScope.stream = feature;
$compile(args.leafletEvent.popup._contentNode)(newScope);
});
Never done it on ionic indeed, but I'm interested on your return.
I've got a page with multiple TinyMce editors and i have drag and drop feature enabled which allows me to change the order of each items.
But as i drag-drop an editor its content gets removed.
See the screen shots :
Before Drag-Drop
After Drag-Drop
Finally fixed the issue...
The solution is to first shut down the tinymce instance (id needed!)
tinymce.execCommand('mceRemoveControl',true,'editor_id');
then do the DOM action and reinit the tinymce instance
tinymce.execCommand('mceAddControl',true,'editor_id');
Add following code on drag end event:
onDragEnd(event: any) {
var tinymceId = 'tinymceId_' + event.source.data.index; //get selected element id
tinymce.get(tinymceId ).remove(); //remove existing instance
$('#' + tinymceId ).closest('.mce-tinymce.mce-container').show();
tinymce.init({id: tinymceId , selector: '#' + tinymceId , height: 200}; //you can add other properties into init()
}
I recently started to develop a class that manipulates over CodeMirror - dynamically links necessary libraries, provides comunication with server and so on.
Because of that I hold everything in variables, and upon initialisation, the CodeMirror is appended to DOM node which is not within document tree. Just like that:
var holder = document.createElement("div");
var textarea = document.createElement("textarea");
holder.appendChild(textarea);
this.editor = CodeMirror.fromTextArea(textarea, {/*options*/});
Then I append the DIV myself when .show() method is called upon my class:
this.show(node) {
if(this.editor!=null) {
node.parentNode.replaceChild(holder,node);
}
}
The editor is OK since click into it and try to draw, or after I resize window. But before, I just see blank area with disabled scroll bar. I believe this is because of way I initialise whole CodeMirror.
So what should I do to make it work, if I want to keep my class structure?
Link to live code (not for IE).
A more direct way would be to call .refresh() on the CodeMirror instance after showing it. See http://codemirror.net/doc/manual.html#refresh
So I solved the issue very simply - I dispatch onresize event after adding editor to dom tree:
node.parentNode.replaceChild(holder,node);
var evt = document.createEvent('UIEvents');
evt.initUIEvent('resize', true, false,window,0);
window.dispatchEvent(evt);
Based on How to dispatch resize event? question.
I've added the Overlay effect of jQuery Tools to my site, using the "Minimum Setup". Unfortunately when the user wants to close it, he must target this tiny circle on the upper right. Usability suffers this way. It would be far better if the user could simply click anywhere to close it.
Can I modify or add some code so it would close the overlay no matter where to user clicks? Or maybe when he clicks outside of the overlay? I couldn't find any notes on that in the docs.
Or maybe when he clicks outside of the overlay?
Check the docs ('Configuration' part):
closeOnClick (default: true)
By default, overlays are closed when the mouse is clicked outside the overlay area. Setting this property to false suppresses this behaviour which is suitable for modal dialogs.
I.e., this functionality is already enabled by default. If it's not working, you might want to show us your overlay configuration.
#NikitaRybak The closeOnClick only works when you're using a expose/mask. I've modified the overlay code to work without a expose/mask..
Tidy the minified javascript..
locate this in the minified code:
b.closeOnClick && a(document).bind("click." + n, function(l) {
and insert this to the function instead
if (!a(l.target).hasClass('overlay') && !a(l.target).hasClass('apple') && !a(l.target).parents('.overlay', f).length && !a(l.target).parents('[rel="#' + a(f).attr('id') + '"]').length && !(a(l.target).attr('rel') == '#' + a(f).attr('id'))) { c.close(l); }
Now if you're using the apple effect, find this in the apple effect code:
b = h('<img src="' + b + '"/>');
and insert class="apple" so it becomes:
b = h('<img class="apple" src="' + b + '"/>');
Hope this helps if anyone needs it..
Try (quick and dirty):
$(document).click(function(){
$('.simple_overlay:visible .close').click();
});
During my experiments I found out that even official jQuery Tools standalone demo doesn't close overlay correctly. For example when I click under the overlayed image it closes but when I click on the right or left side outside of the overlay it doesn't close.
So, in my case I used next solution to close overlay on click anywhere on the document:
<script type="text/javascript">
function closeOverlay(){
$('.overlay:visible .close').click();
}
document.addEventListener('click',closeOverlay,true);
</script>
May be it's dirty too but it works for me.