Hide Giphies in Slack
Table Of Contents
Going through how to hide giphy posts in Slack
TL;DR: if you just want the script, it's here. Works with TamperMonkey (Chrome) and GreaseMonkey (Firefox).
Slack has a somewhat funny /giphy command, where you can type /giphy whatever
and it will display a random GIF of whatever. As cute and funny as they can be,
I have discovered that this might get annoying when your entire channel is full
of nothing but GIFs. Now, Slack does have preferences to not show the image by
default, but it still wastes space. So, I decided to find a way to remove them.
Note: This only works if you access Slack through a browser, not the app, as there's no way (that I know of) to insert a script into the app
Easy Firefox way
My first idea was to use the userContent.css in Firefox. If you aren't familiar
with userContent.css, it's a css file that lives [in your Firefox profile][5],
and allows you to add CSS that will be inserted into every page. AFAIK, there
isn't an equivalent to this in any other browser (Chrome had something similar,
but it was removed) In slack, the wrapper of giphy images has a
data-real-src
attribute, which I'm assuming is for lazy loading images. So the
easy method is to put [data-real-src*="giphy.com"] {display: none}
in the
userContent.css in your profile.
That sort of works, it hides the image, but doesn't hide the command to show it. Looking at the DOM, there was no way to differentiate between giphy commands and normal commands. So, JavaScript is necessary if we want to truly hide the giphy posts.
TamperMonkey script (part 1)
Slack has a interesting DOM structure, where the image is inside a <span>
inside a <div>
. So from the image, we would need to get the ancestor two
levels up from the image. The idea is simple, use
[document.querySelectorAll
][6] to find all the giphy image elements and loop
through them. For each element, go two levels up the tree by calling
[parentElement
][7] twice, and then hide that element. Or, in code:
var giphies = document.querySelectorAll('div[data-real-src*="giphy.com"]');
for (var i = 0; i < giphies.length; i++) {
giphies[i].parentElement.parentElement.style.display = "none";
}
But Slack gets all the messages using AJAX, so they aren't in the DOM when the
load
event fires. Poking around with the inspector, I found that the <body>
element has a class of loading
to start, and then that class is removed when
all the messages are shown. Using that, I could rig up a check using
setInterval
to wait until the class is removed:
var timeout;
function hideGiphies() {
if (!document.body.classList.contains('loading')) {
var giphies = document.querySelectorAll('div[data-real-src*="giphy.com"]');
for (var i = 0; i < giphies.length; i++) {
giphies[i].parentElement.parentElement.style.display = "none";
}
window.clearInterval(timeout);
}
}
document.addEventListener('load', function() {
timeout = window.setInterval(hideGiphies, 200);
});
Now we have a script that reliably removes all giphy images on page load.
Except it doesn't (part 2)
Slack doesn't load all the messages for a channel to start. If you scroll up, it will load more messages, which is a problem for two reasons: 1) The new messages will have the giphy images, because they weren't in the DOM to begin with, and 2) All the GIFs that were hidden will be shown again, because for some reason all the message divs are deleted and then reinserted into the DOM. Not feeling like digging into Slack's JavaScript, I opted to use mutation observers instead.
Mutation observers are a newer addition to the DOM API. They are similar to
the Event
object in that they bind a function to be called on a change, or
mutation, of an element. These mutations can observe a number of different
properties, including child node changes. Stepping through the "loading
more" function, I found a wrapper div where all the nodes got deleted and then
added back.
With a mutation observer attached to that wrapper div, we get the following script:
var timeout;
var observer = new MutationObserver(function(mutations) {
mutations.forEach(function(mutation) {
if (mutation.addedNodes.length !== 0) hideGiphies();
});
});
function hideGiphies() {
if (!document.body.classList.contains('loading')) {
var giphies = document.querySelectorAll('div[data-real-src*="giphy.com"]');
for (var i = 0; i < giphies.length; i++) {
giphies[i].parentElement.parentElement.style.display = "none";
}
var target = document.querySelector('#msgs_div');
observer.observe(target, {childList: true});
window.clearInterval(timeout);
}
}
document.addEventListener('DOMContentLoaded', function() {
timeout = window.setInterval(hideGiphies, 200);
});
Note that I switched to DOMContentLoaded
instead of load
, because load
was
taking a while to fire, depending on the amount of images. The difference
between the two is DOMContentLoaded
will fire when the HTML is finished
parsing, while load
will fire only after all the external resources have
finished downloading.
Conclusion
So, there it is. A simple script to hide giphies in Slack. If you missed the link at the top, here it is again. It's released into the public domain with the CC0 license, so you can take it and do with it as you please. Maybe port it to hipchat or gitter, or hide Slackbot's auto-responses. If you do make something cool and want me to link it here, shoot me an email (see footer for address) with the link and what you did, and maybe we can start a collection of Slack scripts!