Select empty element in TinyMCE on click - javascript

I would like to have the behaviour of the img element applied to another inline element: when clicking on it, it should get selected.
Specifically, I'd like to have an empty q or span tag and when the user clicks on it he should be able to add some attributes. I've written a plugin that works similar to the link-button but it doesn't work for empty elements because they never get selected in the first place. The reason for all this is that I want to replace these elements on the server with a footnote generated from the saved attributes.
I found how it's done for the img element, from tinymce/jscripts/tiny_mce_src.js:
// Workaround for bug, http://bugs.webkit.org/show_bug.cgi?id=12250
// WebKit can't even do simple things like selecting an image
// This also fixes so it's possible to select mceItemAnchors
if (tinymce.isWebKit) {
t.onClick.add(function(ed, e) {
e = e.target;
// Needs tobe the setBaseAndExtend or it will fail to select floated images
if (e.nodeName == 'IMG' || (e.nodeName == 'A' && dom.hasClass(e, 'mceItemAnchor'))) {
t.selection.getSel().setBaseAndExtent(e, 0, e, 1);
t.nodeChanged();
}
});
}
But when I try to copy and adapt this I run into the problem taht e.target is already the surrounding paragraph element instead of the empty q element I clicked on (I've styled it with CSS's :before pseudoclass). At least on WebKit that's what happens. In Firefox it actually works, when using a double click instead of a single click.
Thanks for your help.

I finally got it working! Thank you Thariama for your help :)
Note: important is that the q element is styled with display: inline-block; Otherwise you'll never be able to select it.
//makes an empty q.citation tag selectable
ed.onClick.add(function(ed, e) {
e = e.target;
var dom = tinyMCE.activeEditor.dom;
if (e.nodeName == 'Q' && dom.hasClass(e, 'citation') ) {
ed.selection.select(e);
//add class 'selected' for styling
dom.addClass(e, 'selected');
ed.onNodeChange.add(function(ed, cm, n) {
//remove class 'selected'
dom.removeClass(e, 'selected');
});
}
});

Related

Run a Javascript function when clicking outside div1 AND div2

Trying to run a Javascript function only if click outside div1 AND div2. If clicked inside div1 or div2, nothing should happen. Any idea how I could achieve that? Example: I would need div2 to be set display: noneand additionally add styles to div1 when clicked outside the divs.
var x = document.getElementById("mega-menu");
document.addEventListener('mouseup', function(e) {
if (!(x.contains(e.target) || document.getElementById("menu-item-136").contains(e.target))) {
x.style.display = 'none';
var elem = document.querySelectorAll("#menu-item-136");
elem.forEach((elem) => {
elem.style.backgroundColor = "red";
});
}
});
This is the furthest I could get. But at the moment, div2 (mega-menu) also gets hidden when clicked on the menu item... Hope you understand what I mean...
Thanks
Can you complete your sample code above? I've added elements to your snippet, but can't duplicate your problem. It seems to be working for me.
Also a couple of off-topic best practices:
the HTML standard doesn't allow for elements with duplicate IDs. If you have more than one element with id #menu-item-136, you should be using a class instead of an ID.
Get both elements outside the event listener...more efficient (like you're already doing with #mega-menu)
Instead of having an if statement with a nested block, do an if...return instead. That way your code is less indented.
var x = document.getElementById('mega-menu');
var y = document.getElementById('menu-item-136');
document.addEventListener('mouseup', function(e) {
if (x.contains(e.target) || y.contains(e.target)) return;
x.style.display = 'none';
y.style.backgroundColor = 'red';
});
div { border: 1px solid black; }
<div id='mega-menu'>mega menu</div>
<div id='menu-item-136'>menu 136</div>
Looks like from this code,
document.querySelectorAll("#menu-item-136");
There are multiple elements with id "menu-item-136" as you are looping through the result.
First of all If there are multiple elements with same Id, It is not a valid HTML.
Thats why
document.getElementById("menu-item-136").contains(e.target)
this results in false, as it may have selected wrong element other that what you have clicked.
Even if you use class to get the elements, it may still fail as querySelector based on class will still results in returning multiple elements.
Instead of checking if your target element is contained in the div's - use a check to see if the element parent hierarchy have class name that you add for menu-item div.
if( e.target.closest(".container-class"))
So now you are just checking if current target have any parent with menu-item's class.

Detect click on body on empty spaces - jQuery selectable deselect

I have a jQuery selectable lislt and I want to deselect the items when clicked outside the list.
Initially I used : $('body').click(...) which worked but it detects click on all the elements inside the body. I want to detect it except for clickable elements such as tags like a, button, input, select etc.
So I modified my code as follows:
$("body").click(function (event) {
var tagname = event.target.tagName.toLowerCase();
if (tagname != "a" && tagname != "button" && tagname != "input") {
$('#selectable .ui-selected').removeClass('ui-selected');
}
});
which does not look like an elegant way to do it. And it would even miss certain elements if not defined.
Is there a better way to do this? A way to detect empty spaces in body with no text or click functions attached?

Bootstrap list-items activating on embedded span

Sorry for the title, I found no better way to describe it.
I have a list-group and want the buttons to show a specific color when active.
but somehow, the embedded spans capture the click and seem to not count as part of the a.
how can I fix this?
I want the button to change color, no matter where I click (on span or anywhere else)
The code is here:
https://jsfiddle.net/zj6uwmvu/
thanks
Here is the revised code for your click handler. If the event target is not a link, it means that the child badge was clicked. If this is the case, we find the closest link (the parent) and assign it as the target.
$('.location').find('.location-picker-list .list-group-item').on('click', function(e) {
var target = $(event.target);
if (!target.is('a')) {
target = target.parent('a')
}
e.preventDefault()
target.closest('.list-group').children(".active").removeClass('active')
target.addClass('active')
})
https://jsfiddle.net/zj6uwmvu/11/
instead of getting the item by "a", try getting it by its class like this:
.list-group-item.active, .list-group-item.active:focus, .list-group-item.active:hover{
background-color: red; //instead of red put the color that you want.
}

Check if a div has any children

I have the below code that checks to see if any of my divs has the class "wrong" and if so it shows the jQuery UI dialog box. However i want to extend the code so that it checks those divs and if there are any that are empty it should not show the dialog box.
I've looked around and some people are suggesting using children().length > 0 to accomplish this, but I'm not sure how to write this with my code.
js:
$("#run").click(function() {
if ($("[id^='Drop']").hasClass("wrong")) {
$("#dialog1").dialog("open");
}
});
The following selectors could be used to test if an element is empty or not:
:empty matches elements that have no children (thus, empty)+
:parent matches elements that have children+
Now, rephrasing your statement:
... so that it checks those wrong divs and if
there are any that are empty they are all full it should
not show the dialog box.
You would write:
var $allWrong = $("id[^='Drop'].wrong"),
$notEmpty = $wrong.filter(":parent");
if ($allWrong.length && $allWrong === $notEmpty) {
// show dialog
}
+ Text nodes are counted when counting children. <span> </span> contains a text node which contains a whitespace. Therefore it matches :parent and does not match :empty.
The logic consists of two parts:
Finding elements with id property starting with "Drop" and having the .wrong class.
Checking whether any of those elements are empty.
To do this, I'm saving the first step in an intermediate variable, before doing the final condition:
var $wrongFields = $('[id^="Drop"].wrong');
if ($wrongFields.length && !$wrongFields.filter(':empty').length) {
// at least one field exists with:
// - id starting with "Drop"
// - class of "wrong"
// and none of those fields are empty
$("#dialog1").dialog("open");
}
Demo
This would also work
$("#run").click(function(){
if ($("[id^='Drop']").hasClass("wrong") && $("[id^='Drop'].wrong:empty").length ) {
$( "#dialog1" ).dialog( "open" );
}
});

Testing if elements are hidden

Ok guys so I am testing if elements are hidden, problem is if the parent element is hidden then the children are considered hidden as well so the line that I append is being appending even if the children elements that aren't technically "display:none"
function checkIfVisible() {
var checkLeft= $('#left').children('.module').filter(':hidden');
if(checkLeft.length > 0) {
if(!$('.resetLeft').length) {
left.prepend('<span class="resetLeft">Reset Left Widgets</span>');
}
}
I run this script on page load, and when ever any of the children elements are clicked to be hidden. How do I stop the script from assigning the prepended element from being added if the parent elements is hidden but none of the children are hidden (Display:none) in style?
So basically.
If #left is hidden (parent) and none of the children are I don't want the resetLeft to be prepended. But if #left is hidden and 1 or more of the children of it are display:none then prepended. even if left isn't hidden I need to this work as well.
This should do it:
var visible = $('#left').is(':hidden') && $('#left').children('.module').filter(function() {
return this.style.display === 'none';
}).length === 0;
But this seems like a hack. Why are you forced to do this?
I took what Blender sort of said to do but I changed it around to work just the way I wanted. I used the && and logical operator...
function checkIfVisible() {
var checkLeft=
$('#left').is(':visible')
&&
$('#left').children('.module').filter(':hidden').length > 0;
if(checkLeft) {
if(!$('.resetLeft').length) {
left.prepend('<span class="resetLeft">Reset Left Widgets</span>');
}
}
This worked wonderfully for me, and all I did was add the function to the click function of showing the #left element!

Categories

Resources